nx 21.0.0-beta.0 → 21.0.0-beta.10

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 (242) hide show
  1. package/.eslintrc.json +5 -1
  2. package/migrations.json +5 -35
  3. package/package.json +12 -12
  4. package/release/index.d.ts +1 -1
  5. package/release/index.js +2 -1
  6. package/schemas/nx-schema.json +182 -35
  7. package/schemas/project-schema.json +5 -0
  8. package/src/adapter/compat.d.ts +1 -1
  9. package/src/adapter/compat.js +3 -0
  10. package/src/command-line/add/add.js +6 -16
  11. package/src/command-line/affected/command-object.js +6 -6
  12. package/src/command-line/examples.js +0 -4
  13. package/src/command-line/exec/command-object.js +1 -1
  14. package/src/command-line/generate/generator-utils.js +8 -3
  15. package/src/command-line/import/import.js +1 -1
  16. package/src/command-line/init/command-object.js +18 -6
  17. package/src/command-line/init/configure-plugins.d.ts +6 -7
  18. package/src/command-line/init/configure-plugins.js +52 -38
  19. package/src/command-line/init/implementation/add-nx-to-turborepo.d.ts +4 -0
  20. package/src/command-line/init/implementation/add-nx-to-turborepo.js +49 -0
  21. package/src/command-line/init/implementation/check-compatible-with-plugins.js +7 -1
  22. package/src/command-line/init/implementation/deduce-default-base.d.ts +1 -0
  23. package/src/command-line/init/implementation/deduce-default-base.js +53 -0
  24. package/src/command-line/init/implementation/react/add-vite-commands-to-package-scripts.js +6 -4
  25. package/src/command-line/init/implementation/react/index.d.ts +1 -1
  26. package/src/command-line/init/implementation/react/index.js +32 -185
  27. package/src/command-line/init/implementation/react/write-vite-config.js +19 -3
  28. package/src/command-line/init/implementation/utils.d.ts +6 -2
  29. package/src/command-line/init/implementation/utils.js +110 -45
  30. package/src/command-line/init/init-v1.js +1 -1
  31. package/src/command-line/init/init-v2.d.ts +1 -0
  32. package/src/command-line/init/init-v2.js +70 -39
  33. package/src/command-line/migrate/migrate-ui-api.d.ts +58 -0
  34. package/src/command-line/migrate/migrate-ui-api.js +227 -0
  35. package/src/command-line/migrate/migrate.d.ts +16 -3
  36. package/src/command-line/migrate/migrate.js +134 -101
  37. package/src/command-line/nx-commands.js +19 -5
  38. package/src/command-line/register/command-object.d.ts +6 -0
  39. package/src/command-line/{activate-powerpack → register}/command-object.js +9 -9
  40. package/src/command-line/register/register.d.ts +2 -0
  41. package/src/command-line/register/register.js +9 -0
  42. package/src/command-line/release/changelog.js +18 -15
  43. package/src/command-line/release/command-object.d.ts +8 -0
  44. package/src/command-line/release/command-object.js +9 -0
  45. package/src/command-line/release/config/config.d.ts +8 -7
  46. package/src/command-line/release/config/config.js +139 -45
  47. package/src/command-line/release/config/use-legacy-versioning.d.ts +2 -0
  48. package/src/command-line/release/config/use-legacy-versioning.js +9 -0
  49. package/src/command-line/release/index.d.ts +4 -0
  50. package/src/command-line/release/index.js +6 -1
  51. package/src/command-line/release/plan-check.js +6 -3
  52. package/src/command-line/release/plan.js +7 -3
  53. package/src/command-line/release/publish.js +7 -3
  54. package/src/command-line/release/release.js +8 -3
  55. package/src/command-line/release/utils/batch-projects-by-generator-config.js +6 -3
  56. package/src/command-line/release/utils/git.d.ts +3 -2
  57. package/src/command-line/release/utils/git.js +65 -9
  58. package/src/command-line/release/utils/github.js +3 -1
  59. package/src/command-line/release/utils/resolve-semver-specifier.d.ts +2 -1
  60. package/src/command-line/release/utils/resolve-semver-specifier.js +2 -1
  61. package/src/command-line/release/utils/semver.d.ts +8 -0
  62. package/src/command-line/release/utils/semver.js +8 -0
  63. package/src/command-line/release/utils/shared-legacy.d.ts +25 -0
  64. package/src/command-line/release/utils/shared-legacy.js +2 -0
  65. package/src/command-line/release/utils/shared.d.ts +11 -17
  66. package/src/command-line/release/version/derive-specifier-from-conventional-commits.d.ts +7 -0
  67. package/src/command-line/release/version/derive-specifier-from-conventional-commits.js +47 -0
  68. package/src/command-line/release/version/deriver-specifier-from-version-plans.d.ts +8 -0
  69. package/src/command-line/release/version/deriver-specifier-from-version-plans.js +59 -0
  70. package/src/command-line/release/version/project-logger.d.ts +8 -0
  71. package/src/command-line/release/version/project-logger.js +45 -0
  72. package/src/command-line/release/version/release-group-processor.d.ts +252 -0
  73. package/src/command-line/release/version/release-group-processor.js +1057 -0
  74. package/src/command-line/release/version/resolve-current-version.d.ts +32 -0
  75. package/src/command-line/release/version/resolve-current-version.js +241 -0
  76. package/src/command-line/release/version/test-utils.d.ts +93 -0
  77. package/src/command-line/release/version/test-utils.js +415 -0
  78. package/src/command-line/release/version/topological-sort.d.ts +9 -0
  79. package/src/command-line/release/version/topological-sort.js +41 -0
  80. package/src/command-line/release/version/version-actions.d.ts +171 -0
  81. package/src/command-line/release/version/version-actions.js +195 -0
  82. package/src/command-line/release/version-legacy.d.ts +46 -0
  83. package/src/command-line/release/version-legacy.js +453 -0
  84. package/src/command-line/release/version.d.ts +0 -40
  85. package/src/command-line/release/version.js +84 -262
  86. package/src/command-line/repair/repair.js +0 -1
  87. package/src/command-line/report/report.d.ts +7 -3
  88. package/src/command-line/report/report.js +52 -18
  89. package/src/command-line/run/command-object.js +2 -2
  90. package/src/command-line/run/executor-utils.d.ts +6 -1
  91. package/src/command-line/run/executor-utils.js +10 -1
  92. package/src/command-line/run/run.js +2 -2
  93. package/src/command-line/run-many/command-object.js +2 -2
  94. package/src/command-line/yargs-utils/shared-options.d.ts +4 -0
  95. package/src/command-line/yargs-utils/shared-options.js +20 -0
  96. package/src/config/misc-interfaces.d.ts +11 -1
  97. package/src/config/nx-json.d.ts +160 -16
  98. package/src/config/project-graph.d.ts +4 -2
  99. package/src/config/project-graph.js +8 -0
  100. package/src/config/workspace-json-project-json.d.ts +2 -2
  101. package/src/core/graph/main.js +1 -1
  102. package/src/core/graph/runtime.js +1 -1
  103. package/src/core/graph/styles.css +2 -2
  104. package/src/core/graph/styles.js +1 -1
  105. package/src/daemon/client/client.d.ts +2 -0
  106. package/src/daemon/client/client.js +15 -0
  107. package/src/daemon/message-types/glob.d.ts +7 -0
  108. package/src/daemon/message-types/glob.js +9 -1
  109. package/src/daemon/message-types/hash-glob.d.ts +6 -0
  110. package/src/daemon/message-types/hash-glob.js +9 -1
  111. package/src/daemon/server/handle-glob.d.ts +1 -0
  112. package/src/daemon/server/handle-glob.js +8 -0
  113. package/src/daemon/server/handle-hash-glob.d.ts +1 -0
  114. package/src/daemon/server/handle-hash-glob.js +8 -0
  115. package/src/daemon/server/logger.js +2 -1
  116. package/src/daemon/server/server.js +7 -0
  117. package/src/devkit-internals.d.ts +3 -2
  118. package/src/devkit-internals.js +5 -1
  119. package/src/executors/run-commands/run-commands.impl.d.ts +2 -5
  120. package/src/executors/run-commands/run-commands.impl.js +14 -42
  121. package/src/executors/run-commands/running-tasks.d.ts +9 -4
  122. package/src/executors/run-commands/running-tasks.js +103 -30
  123. package/src/executors/run-script/run-script.impl.js +4 -3
  124. package/src/generators/internal-utils/format-changed-files-with-prettier-if-available.js +8 -0
  125. package/src/generators/testing-utils/create-tree.js +5 -1
  126. package/src/migrations/{update-17-0-0/rm-default-collection-npm-scope.d.ts → update-21-0-0/release-version-config-changes.d.ts} +1 -1
  127. package/src/migrations/update-21-0-0/release-version-config-changes.js +111 -0
  128. package/src/native/index.d.ts +98 -19
  129. package/src/native/index.js +16 -2
  130. package/src/native/native-bindings.js +7 -0
  131. package/src/native/nx.wasi-browser.js +20 -19
  132. package/src/native/nx.wasi.cjs +20 -19
  133. package/src/native/nx.wasm32-wasi.wasm +0 -0
  134. package/src/nx-cloud/nx-cloud-tasks-runner-shell.js +3 -3
  135. package/src/plugins/js/lock-file/lock-file.js +28 -13
  136. package/src/plugins/js/lock-file/utils/package-json.d.ts +1 -1
  137. package/src/plugins/js/lock-file/utils/package-json.js +8 -6
  138. package/src/plugins/js/lock-file/utils/pnpm-normalizer.js +3 -3
  139. package/src/plugins/js/lock-file/yarn-parser.js +85 -39
  140. package/src/plugins/js/project-graph/affected/lock-file-changes.js +1 -0
  141. package/src/plugins/js/project-graph/build-dependencies/explicit-project-dependencies.js +1 -1
  142. package/src/plugins/js/project-graph/build-dependencies/target-project-locator.d.ts +10 -1
  143. package/src/plugins/js/project-graph/build-dependencies/target-project-locator.js +59 -6
  144. package/src/plugins/js/utils/packages.js +22 -3
  145. package/src/plugins/js/utils/register.js +1 -0
  146. package/src/plugins/js/utils/typescript.js +3 -3
  147. package/src/plugins/package-json/create-nodes.d.ts +1 -1
  148. package/src/plugins/package-json/create-nodes.js +4 -2
  149. package/src/project-graph/affected/locators/project-glob-changes.js +2 -2
  150. package/src/project-graph/error-types.js +32 -2
  151. package/src/project-graph/file-utils.d.ts +1 -10
  152. package/src/project-graph/file-utils.js +2 -77
  153. package/src/project-graph/nx-deps-cache.js +7 -2
  154. package/src/project-graph/plugins/get-plugins.js +2 -1
  155. package/src/project-graph/plugins/in-process-loader.js +1 -1
  156. package/src/project-graph/plugins/isolation/plugin-worker.js +12 -6
  157. package/src/project-graph/plugins/loaded-nx-plugin.d.ts +2 -1
  158. package/src/project-graph/plugins/loaded-nx-plugin.js +3 -7
  159. package/src/project-graph/plugins/public-api.d.ts +1 -1
  160. package/src/project-graph/plugins/utils.d.ts +2 -2
  161. package/src/project-graph/plugins/utils.js +2 -2
  162. package/src/project-graph/project-graph.js +5 -2
  163. package/src/project-graph/utils/project-configuration-utils.d.ts +3 -3
  164. package/src/project-graph/utils/project-configuration-utils.js +54 -21
  165. package/src/project-graph/utils/retrieve-workspace-files.d.ts +1 -1
  166. package/src/project-graph/utils/retrieve-workspace-files.js +14 -18
  167. package/src/tasks-runner/batch/batch-messages.d.ts +2 -0
  168. package/src/tasks-runner/batch/run-batch.js +3 -4
  169. package/src/tasks-runner/cache.d.ts +20 -6
  170. package/src/tasks-runner/cache.js +104 -20
  171. package/src/tasks-runner/create-task-graph.d.ts +0 -1
  172. package/src/tasks-runner/create-task-graph.js +11 -11
  173. package/src/tasks-runner/default-tasks-runner.js +5 -14
  174. package/src/tasks-runner/forked-process-task-runner.d.ts +8 -3
  175. package/src/tasks-runner/forked-process-task-runner.js +59 -46
  176. package/src/tasks-runner/init-tasks-runner.d.ts +15 -1
  177. package/src/tasks-runner/init-tasks-runner.js +62 -2
  178. package/src/tasks-runner/is-tui-enabled.d.ts +2 -0
  179. package/src/tasks-runner/is-tui-enabled.js +64 -0
  180. package/src/tasks-runner/life-cycle.d.ts +14 -3
  181. package/src/tasks-runner/life-cycle.js +37 -2
  182. package/src/tasks-runner/life-cycles/task-history-life-cycle-old.d.ts +2 -0
  183. package/src/tasks-runner/life-cycles/task-history-life-cycle-old.js +15 -7
  184. package/src/tasks-runner/life-cycles/task-history-life-cycle.d.ts +5 -0
  185. package/src/tasks-runner/life-cycles/task-history-life-cycle.js +35 -5
  186. package/src/tasks-runner/life-cycles/tui-summary-life-cycle.d.ts +18 -0
  187. package/src/tasks-runner/life-cycles/tui-summary-life-cycle.js +229 -0
  188. package/src/tasks-runner/pseudo-terminal.d.ts +10 -7
  189. package/src/tasks-runner/pseudo-terminal.js +37 -35
  190. package/src/tasks-runner/run-command.d.ts +4 -1
  191. package/src/tasks-runner/run-command.js +220 -42
  192. package/src/tasks-runner/running-tasks/node-child-process.js +4 -11
  193. package/src/tasks-runner/running-tasks/running-task.d.ts +3 -0
  194. package/src/tasks-runner/running-tasks/shared-running-task.d.ts +14 -0
  195. package/src/tasks-runner/running-tasks/shared-running-task.js +30 -0
  196. package/src/tasks-runner/task-env.d.ts +1 -4
  197. package/src/tasks-runner/task-env.js +2 -0
  198. package/src/tasks-runner/task-orchestrator.d.ts +26 -10
  199. package/src/tasks-runner/task-orchestrator.js +212 -57
  200. package/src/tasks-runner/tasks-runner.d.ts +1 -0
  201. package/src/tasks-runner/tasks-schedule.d.ts +1 -0
  202. package/src/tasks-runner/tasks-schedule.js +9 -0
  203. package/src/tasks-runner/utils.d.ts +2 -2
  204. package/src/tasks-runner/utils.js +18 -12
  205. package/src/utils/child-process.d.ts +4 -0
  206. package/src/utils/child-process.js +23 -30
  207. package/src/utils/command-line-utils.d.ts +1 -1
  208. package/src/utils/find-matching-projects.js +2 -2
  209. package/src/utils/git-utils.d.ts +1 -1
  210. package/src/utils/git-utils.js +8 -3
  211. package/src/utils/handle-errors.js +15 -0
  212. package/src/utils/is-ci.js +4 -1
  213. package/src/utils/is-using-prettier.d.ts +3 -0
  214. package/src/utils/is-using-prettier.js +62 -0
  215. package/src/utils/nx-key.d.ts +7 -0
  216. package/src/utils/nx-key.js +52 -0
  217. package/src/utils/package-json.d.ts +1 -1
  218. package/src/utils/package-json.js +16 -2
  219. package/src/utils/package-manager.js +2 -2
  220. package/src/utils/path.js +1 -1
  221. package/src/utils/require-nx-key.d.ts +1 -0
  222. package/src/utils/require-nx-key.js +22 -0
  223. package/src/utils/workspace-context.d.ts +2 -0
  224. package/src/utils/workspace-context.js +16 -0
  225. package/src/command-line/activate-powerpack/activate-powerpack.d.ts +0 -2
  226. package/src/command-line/activate-powerpack/activate-powerpack.js +0 -34
  227. package/src/command-line/activate-powerpack/command-object.d.ts +0 -6
  228. package/src/command-line/init/implementation/react/write-craco-config.d.ts +0 -1
  229. package/src/command-line/init/implementation/react/write-craco-config.js +0 -61
  230. package/src/migrations/update-17-0-0/move-cache-directory.d.ts +0 -2
  231. package/src/migrations/update-17-0-0/move-cache-directory.js +0 -35
  232. package/src/migrations/update-17-0-0/rm-default-collection-npm-scope.js +0 -72
  233. package/src/migrations/update-17-0-0/use-minimal-config-for-tasks-runner-options.d.ts +0 -2
  234. package/src/migrations/update-17-0-0/use-minimal-config-for-tasks-runner-options.js +0 -122
  235. package/src/migrations/update-17-2-0/move-default-base.d.ts +0 -5
  236. package/src/migrations/update-17-2-0/move-default-base.js +0 -21
  237. package/src/migrations/update-17-3-0/nx-release-path.d.ts +0 -3
  238. package/src/migrations/update-17-3-0/nx-release-path.js +0 -47
  239. package/src/migrations/update-18-0-0/disable-crystal-for-existing-workspaces.d.ts +0 -2
  240. package/src/migrations/update-18-0-0/disable-crystal-for-existing-workspaces.js +0 -11
  241. package/src/utils/powerpack.d.ts +0 -5
  242. package/src/utils/powerpack.js +0 -33
@@ -0,0 +1,1057 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ReleaseGroupProcessor = exports.BUMP_TYPE_REASON_TEXT = void 0;
4
+ const semver = require("semver");
5
+ const config_1 = require("../config/config");
6
+ const git_1 = require("../utils/git");
7
+ const resolve_semver_specifier_1 = require("../utils/resolve-semver-specifier");
8
+ const version_1 = require("../version");
9
+ const derive_specifier_from_conventional_commits_1 = require("./derive-specifier-from-conventional-commits");
10
+ const deriver_specifier_from_version_plans_1 = require("./deriver-specifier-from-version-plans");
11
+ const project_logger_1 = require("./project-logger");
12
+ const resolve_current_version_1 = require("./resolve-current-version");
13
+ const topological_sort_1 = require("./topological-sort");
14
+ const version_actions_1 = require("./version-actions");
15
+ exports.BUMP_TYPE_REASON_TEXT = {
16
+ DEPENDENCY_WAS_BUMPED: ', because a dependency was bumped, ',
17
+ USER_SPECIFIER: ', from the given specifier, ',
18
+ PROMPTED_USER_SPECIFIER: ', from the prompted specifier, ',
19
+ CONVENTIONAL_COMMITS: ', derived from conventional commits data, ',
20
+ VERSION_PLANS: ', read from version plan {versionPlanPath}, ',
21
+ DEPENDENCY_ACROSS_GROUPS_WAS_BUMPED: ', because a dependency project belonging to another release group was bumped, ',
22
+ OTHER_PROJECT_IN_FIXED_GROUP_WAS_BUMPED_DUE_TO_DEPENDENCY: ', because of a dependency-only bump to another project in the same fixed release group, ',
23
+ };
24
+ class ReleaseGroupProcessor {
25
+ constructor(tree, projectGraph, nxReleaseConfig, releaseGroups, releaseGroupToFilteredProjects, options) {
26
+ this.tree = tree;
27
+ this.projectGraph = projectGraph;
28
+ this.nxReleaseConfig = nxReleaseConfig;
29
+ this.releaseGroups = releaseGroups;
30
+ this.releaseGroupToFilteredProjects = releaseGroupToFilteredProjects;
31
+ this.options = options;
32
+ /**
33
+ * Stores the relationships between release groups, including their dependencies
34
+ * and dependents. This is used for determining processing order and propagating
35
+ * version changes between related groups.
36
+ */
37
+ this.groupGraph = new Map();
38
+ /**
39
+ * Tracks which release groups have already been processed to avoid
40
+ * processing them multiple times. Used during the group traversal.
41
+ */
42
+ this.processedGroups = new Set();
43
+ /**
44
+ * Keeps track of which projects have already had their versions bumped.
45
+ * This is used to avoid redundant version bumping and to determine which
46
+ * projects need their dependencies updated.
47
+ */
48
+ this.bumpedProjects = new Set();
49
+ /**
50
+ * Cache of release groups sorted in topological order to ensure dependencies
51
+ * are processed before dependents. Computed once and reused throughout processing.
52
+ */
53
+ this.sortedReleaseGroups = [];
54
+ /**
55
+ * Maps each release group to its projects sorted in topological order.
56
+ * Ensures projects are processed after their dependencies within each group.
57
+ */
58
+ this.sortedProjects = new Map();
59
+ /**
60
+ * Track the unique afterAllProjectsVersioned functions involved in the current versioning process,
61
+ * so that we can ensure they are only invoked once per versioning execution.
62
+ */
63
+ this.uniqueAfterAllProjectsVersioned = new Map();
64
+ /**
65
+ * Track the versionActions for each project so that we can invoke certain instance methods.
66
+ */
67
+ this.projectsToVersionActions = new Map();
68
+ /**
69
+ * versionData that will ultimately be returned to the nx release version handler by getVersionData()
70
+ */
71
+ this.versionData = new Map();
72
+ /**
73
+ * Set of all projects that are configured in the nx release config.
74
+ * Used to validate dependencies and identify projects that should be updated.
75
+ */
76
+ this.allProjectsConfiguredForNxRelease = new Set();
77
+ /**
78
+ * Set of projects that will be processed in the current run.
79
+ * This is potentially a subset of allProjectsConfiguredForNxRelease based on filters
80
+ * and dependency relationships.
81
+ */
82
+ this.allProjectsToProcess = new Set();
83
+ /**
84
+ * Caches the current version of each project to avoid repeated disk/registry/git tag lookups.
85
+ * Often used during new version calculation. Will be null if the current version resolver is set to 'none'.
86
+ */
87
+ this.cachedCurrentVersions = new Map();
88
+ /**
89
+ * Caches git tag information for projects that resolve their version from git tags.
90
+ * This avoids performing expensive git operations multiple times for the same project.
91
+ */
92
+ this.cachedLatestMatchingGitTag = new Map();
93
+ /**
94
+ * Temporary storage for dependent project names while building the dependency graph.
95
+ * This is used as an intermediate step before creating the full dependent projects data.
96
+ */
97
+ this.tmpCachedDependentProjects = new Map();
98
+ /**
99
+ * Resolve the data regarding dependent projects for each project upfront so that it remains accurate
100
+ * even after updates are applied to manifests.
101
+ */
102
+ this.originalDependentProjectsPerProject = new Map();
103
+ /**
104
+ * In the case of fixed release groups that are configured to resolve the current version from a registry
105
+ * or a git tag, it would be a waste of time and resources to resolve the current version for each individual
106
+ * project, therefore we maintain a cache of the current version for each applicable fixed release group here.
107
+ */
108
+ this.currentVersionsPerFixedReleaseGroup = new Map();
109
+ /**
110
+ * Cache of project loggers for each project.
111
+ */
112
+ this.projectLoggers = new Map();
113
+ /**
114
+ * Track any version plan files that have been processed so that we can delete them after versioning is complete,
115
+ * while leaving any unprocessed files in place.
116
+ */
117
+ this.processedVersionPlanFiles = new Set();
118
+ /**
119
+ * Certain configuration options can be overridden at the project level, and otherwise fall back to the release group level.
120
+ * Many also have a specific default value if nothing is set at either level. To avoid applying this hierarchy for each project
121
+ * every time such a configuration option is needed, we cache the result per project here.
122
+ */
123
+ this.finalConfigsByProject = new Map();
124
+ /**
125
+ * Maps each project to its release group for quick O(1) lookups.
126
+ * This avoids having to scan through all release groups to find a project.
127
+ */
128
+ this.projectToReleaseGroup = new Map();
129
+ /**
130
+ * Maps each project to its dependents (projects that depend on it).
131
+ * This is the inverse of the projectToDependencies map and enables
132
+ * efficient lookup of dependent projects for propagating version changes.
133
+ */
134
+ this.projectToDependents = new Map();
135
+ /**
136
+ * Maps each project to its dependencies (projects it depends on).
137
+ * Used for building dependency graphs and determining processing order.
138
+ */
139
+ this.projectToDependencies = new Map();
140
+ /**
141
+ * Caches the updateDependents setting for each project to avoid repeated
142
+ * lookups and calculations. This determines if dependent projects should
143
+ * be automatically updated when a dependency changes.
144
+ */
145
+ this.projectToUpdateDependentsSetting = new Map();
146
+ /**
147
+ * To match legacy versioning behavior in the case of semver versions with leading "v" characters,
148
+ * e.g. "v1.0.0", we strip the leading "v" and use the rest of the string as the user given specifier
149
+ * to ensure that it is valid. If it is a non-semver version, we just use the string as is.
150
+ *
151
+ * TODO: re-evaluate if this is definitely what we want... Maybe we can just delegate isValid to the
152
+ * version actions implementation during prompting?
153
+ */
154
+ if (options.userGivenSpecifier?.startsWith('v')) {
155
+ const userGivenSpecifierWithoutLeadingV = options.userGivenSpecifier?.replace(/^v/, '');
156
+ if (semver.valid(userGivenSpecifierWithoutLeadingV)) {
157
+ this.userGivenSpecifier = userGivenSpecifierWithoutLeadingV;
158
+ }
159
+ }
160
+ else {
161
+ this.userGivenSpecifier = options.userGivenSpecifier;
162
+ }
163
+ }
164
+ /**
165
+ * Initialize the processor by building the group graph and preparing for processing.
166
+ * This method must be called before processGroups().
167
+ */
168
+ async init() {
169
+ // Precompute project to release group mapping for O(1) lookups
170
+ this.setupProjectReleaseGroupMapping();
171
+ // Setup projects to process and resolve version actions
172
+ await this.setupProjectsToProcess();
173
+ // Precompute dependency relationships
174
+ await this.precomputeDependencyRelationships();
175
+ // Process dependency graph to find dependents to process
176
+ this.findDependentsToProcess();
177
+ // Build the group graph structure
178
+ for (const group of this.releaseGroups) {
179
+ this.groupGraph.set(group.name, {
180
+ group,
181
+ dependencies: new Set(),
182
+ dependents: new Set(),
183
+ });
184
+ }
185
+ // Process each project within each release group
186
+ for (const [, releaseGroupNode] of this.groupGraph) {
187
+ for (const projectName of releaseGroupNode.group.projects) {
188
+ const projectGraphNode = this.projectGraph.nodes[projectName];
189
+ // Check if the project has been filtered out of explicit versioning before continuing any further
190
+ if (!this.allProjectsToProcess.has(projectName)) {
191
+ continue;
192
+ }
193
+ const versionActions = this.getVersionActionsForProject(projectName);
194
+ const finalConfigForProject = this.getFinalConfigForProject(projectName);
195
+ let latestMatchingGitTag;
196
+ const releaseTagPattern = releaseGroupNode.group.releaseTagPattern;
197
+ // Cache the last matching git tag for relevant projects
198
+ if (finalConfigForProject.currentVersionResolver === 'git-tag') {
199
+ latestMatchingGitTag = await (0, git_1.getLatestGitTagForPattern)(releaseTagPattern, {
200
+ projectName: projectGraphNode.name,
201
+ }, releaseGroupNode.group.releaseTagPatternCheckAllBranchesWhen);
202
+ this.cachedLatestMatchingGitTag.set(projectName, latestMatchingGitTag);
203
+ }
204
+ // Cache the current version for the project
205
+ const currentVersion = await (0, resolve_current_version_1.resolveCurrentVersion)(this.tree, projectGraphNode, releaseGroupNode.group, versionActions, this.projectLoggers.get(projectName), this.currentVersionsPerFixedReleaseGroup, finalConfigForProject, releaseTagPattern, latestMatchingGitTag);
206
+ this.cachedCurrentVersions.set(projectName, currentVersion);
207
+ }
208
+ // Ensure that there is an entry in versionData for each project being processed, even if they don't end up being bumped
209
+ for (const projectName of this.allProjectsToProcess) {
210
+ this.versionData.set(projectName, {
211
+ currentVersion: this.getCurrentCachedVersionForProject(projectName),
212
+ newVersion: null,
213
+ dependentProjects: this.getOriginalDependentProjects(projectName),
214
+ });
215
+ }
216
+ }
217
+ // Build the dependency relationships between groups
218
+ this.buildGroupDependencyGraph();
219
+ // Topologically sort the release groups and projects for efficient processing
220
+ this.sortedReleaseGroups = this.topologicallySortReleaseGroups();
221
+ // Sort projects within each release group
222
+ for (const group of this.releaseGroups) {
223
+ this.sortedProjects.set(group.name, this.topologicallySortProjects(group));
224
+ }
225
+ // Populate the dependent projects data
226
+ await this.populateDependentProjectsData();
227
+ }
228
+ /**
229
+ * Setup mapping from project to release group and cache updateDependents settings
230
+ */
231
+ setupProjectReleaseGroupMapping() {
232
+ for (const group of this.releaseGroups) {
233
+ for (const project of group.projects) {
234
+ this.projectToReleaseGroup.set(project, group);
235
+ // Cache updateDependents setting relevant for each project
236
+ const updateDependents = group.version
237
+ ?.updateDependents || 'auto';
238
+ this.projectToUpdateDependentsSetting.set(project, updateDependents);
239
+ }
240
+ }
241
+ }
242
+ /**
243
+ * Determine which projects should be processed and resolve their version actions
244
+ */
245
+ async setupProjectsToProcess() {
246
+ // Track the projects being directly versioned
247
+ let projectsToProcess = new Set();
248
+ // Precompute all projects in nx release config
249
+ for (const [groupName, group] of Object.entries(this.nxReleaseConfig.groups)) {
250
+ for (const project of group.projects) {
251
+ this.allProjectsConfiguredForNxRelease.add(project);
252
+ // Create a project logger for the project
253
+ this.projectLoggers.set(project, new project_logger_1.ProjectLogger(project));
254
+ // If group filtering is applied and the current group is captured by the filter, add the project to the projectsToProcess
255
+ if (this.options.filters.groups?.includes(groupName)) {
256
+ projectsToProcess.add(project);
257
+ // Otherwise, if project filtering is applied and the current project is captured by the filter, add the project to the projectsToProcess
258
+ }
259
+ else if (this.options.filters.projects?.includes(project)) {
260
+ projectsToProcess.add(project);
261
+ }
262
+ const projectGraphNode = this.projectGraph.nodes[project];
263
+ /**
264
+ * Try and resolve a cached ReleaseGroupWithName for the project. It may not be present
265
+ * if the user filtered by group and excluded this parent group from direct versioning,
266
+ * so fallback to the release group config and apply the name manually.
267
+ */
268
+ let releaseGroup = this.projectToReleaseGroup.get(project);
269
+ if (!releaseGroup) {
270
+ releaseGroup = {
271
+ ...group,
272
+ name: groupName,
273
+ resolvedVersionPlans: false,
274
+ };
275
+ }
276
+ // Resolve the final configuration for the project
277
+ const finalConfigForProject = this.resolveFinalConfigForProject(releaseGroup, projectGraphNode);
278
+ this.finalConfigsByProject.set(project, finalConfigForProject);
279
+ const { versionActionsPath, versionActions, afterAllProjectsVersioned, } = await (0, version_actions_1.resolveVersionActionsForProject)(this.tree, releaseGroup, projectGraphNode, finalConfigForProject);
280
+ if (!this.uniqueAfterAllProjectsVersioned.has(versionActionsPath)) {
281
+ this.uniqueAfterAllProjectsVersioned.set(versionActionsPath, afterAllProjectsVersioned);
282
+ }
283
+ this.projectsToVersionActions.set(project, versionActions);
284
+ }
285
+ }
286
+ // If no filters are applied, process all projects
287
+ if (!this.options.filters.groups?.length &&
288
+ !this.options.filters.projects?.length) {
289
+ projectsToProcess = this.allProjectsConfiguredForNxRelease;
290
+ }
291
+ // If no projects are set to be processed, throw an error. This should be impossible because the filter validation in version.ts should have already caught this
292
+ if (projectsToProcess.size === 0) {
293
+ throw new Error('No projects are set to be processed, please report this as a bug on https://github.com/nrwl/nx/issues');
294
+ }
295
+ this.allProjectsToProcess = new Set(projectsToProcess);
296
+ }
297
+ /**
298
+ * Find all dependents that should be processed due to dependency updates
299
+ */
300
+ findDependentsToProcess() {
301
+ const projectsToProcess = Array.from(this.allProjectsToProcess);
302
+ const allTrackedDependents = new Set();
303
+ const dependentsToProcess = new Set();
304
+ // BFS traversal of dependency graph to find all transitive dependents
305
+ let currentLevel = [...projectsToProcess];
306
+ while (currentLevel.length > 0) {
307
+ const nextLevel = [];
308
+ // Get all dependents for the current level at once
309
+ const dependents = this.getAllNonImplicitDependents(currentLevel);
310
+ // Process each dependent
311
+ for (const dep of dependents) {
312
+ // Skip if we've already seen this dependent or it's already in projectsToProcess
313
+ if (allTrackedDependents.has(dep) ||
314
+ this.allProjectsToProcess.has(dep)) {
315
+ continue;
316
+ }
317
+ // Track that we've seen this dependent
318
+ allTrackedDependents.add(dep);
319
+ // If both the dependent and its dependency have updateDependents='auto',
320
+ // add the dependent to the projects to process
321
+ if (this.hasAutoUpdateDependents(dep)) {
322
+ // Check if any of its dependencies in the current level have auto update
323
+ const hasDependencyWithAutoUpdate = currentLevel.some((proj) => this.hasAutoUpdateDependents(proj) &&
324
+ this.getProjectDependents(proj).has(dep));
325
+ if (hasDependencyWithAutoUpdate) {
326
+ dependentsToProcess.add(dep);
327
+ }
328
+ }
329
+ // Add to next level for traversal
330
+ nextLevel.push(dep);
331
+ }
332
+ // Move to next level
333
+ currentLevel = nextLevel;
334
+ }
335
+ // Add all dependents that should be processed to allProjectsToProcess
336
+ dependentsToProcess.forEach((dep) => this.allProjectsToProcess.add(dep));
337
+ }
338
+ buildGroupDependencyGraph() {
339
+ for (const [releaseGroupName, releaseGroupNode] of this.groupGraph) {
340
+ for (const projectName of releaseGroupNode.group.projects) {
341
+ const projectDeps = this.getProjectDependencies(projectName);
342
+ for (const dep of projectDeps) {
343
+ const dependencyGroup = this.getReleaseGroupNameForProject(dep);
344
+ if (dependencyGroup && dependencyGroup !== releaseGroupName) {
345
+ releaseGroupNode.dependencies.add(dependencyGroup);
346
+ this.groupGraph
347
+ .get(dependencyGroup)
348
+ .dependents.add(releaseGroupName);
349
+ }
350
+ }
351
+ }
352
+ }
353
+ }
354
+ async populateDependentProjectsData() {
355
+ for (const [projectName, dependentProjectNames] of this
356
+ .tmpCachedDependentProjects) {
357
+ const dependentProjectsData = [];
358
+ for (const dependentProjectName of dependentProjectNames) {
359
+ const versionActions = this.getVersionActionsForProject(dependentProjectName);
360
+ const { currentVersion, dependencyCollection } = await versionActions.readCurrentVersionOfDependency(this.tree, this.projectGraph, projectName);
361
+ dependentProjectsData.push({
362
+ source: dependentProjectName,
363
+ target: projectName,
364
+ type: 'static',
365
+ dependencyCollection,
366
+ rawVersionSpec: currentVersion,
367
+ });
368
+ }
369
+ this.originalDependentProjectsPerProject.set(projectName, dependentProjectsData);
370
+ }
371
+ }
372
+ getReleaseGroupNameForProject(projectName) {
373
+ const group = this.projectToReleaseGroup.get(projectName);
374
+ return group ? group.name : null;
375
+ }
376
+ getNextGroup() {
377
+ for (const [groupName, groupNode] of this.groupGraph) {
378
+ if (!this.processedGroups.has(groupName) &&
379
+ Array.from(groupNode.dependencies).every((dep) => this.processedGroups.has(dep))) {
380
+ return groupName;
381
+ }
382
+ }
383
+ return null;
384
+ }
385
+ async processGroups() {
386
+ const processOrder = [];
387
+ // Use the topologically sorted groups instead of getNextGroup
388
+ for (const nextGroup of this.sortedReleaseGroups) {
389
+ // Skip groups that have already been processed (could happen with circular dependencies)
390
+ if (this.processedGroups.has(nextGroup)) {
391
+ continue;
392
+ }
393
+ const allDependenciesProcessed = Array.from(this.groupGraph.get(nextGroup).dependencies).every((dep) => this.processedGroups.has(dep));
394
+ if (!allDependenciesProcessed) {
395
+ // If we encounter a group whose dependencies aren't all processed,
396
+ // it means there's a circular dependency that our topological sort broke.
397
+ // We need to process any unprocessed dependencies first.
398
+ for (const dep of this.groupGraph.get(nextGroup).dependencies) {
399
+ if (!this.processedGroups.has(dep)) {
400
+ await this.processGroup(dep);
401
+ this.processedGroups.add(dep);
402
+ processOrder.push(dep);
403
+ }
404
+ }
405
+ }
406
+ await this.processGroup(nextGroup);
407
+ this.processedGroups.add(nextGroup);
408
+ processOrder.push(nextGroup);
409
+ }
410
+ return processOrder;
411
+ }
412
+ flushAllProjectLoggers() {
413
+ for (const projectLogger of this.projectLoggers.values()) {
414
+ projectLogger.flush();
415
+ }
416
+ }
417
+ deleteProcessedVersionPlanFiles() {
418
+ for (const versionPlanPath of this.processedVersionPlanFiles) {
419
+ this.tree.delete(versionPlanPath);
420
+ }
421
+ }
422
+ getVersionData() {
423
+ return Object.fromEntries(this.versionData);
424
+ }
425
+ /**
426
+ * Invoke the afterAllProjectsVersioned functions for each unique versionActions type.
427
+ * This can be useful for performing actions like updating a workspace level lock file.
428
+ *
429
+ * Because the tree has already been flushed to disk at this point, each afterAllProjectsVersioned
430
+ * function is responsible for returning the list of changed and deleted files that it affected.
431
+ *
432
+ * The root level `release.version.versionActionsOptions` is what is passed in here because this
433
+ * is a one time action for the whole workspace. Release group and project level overrides are
434
+ * not applicable.
435
+ */
436
+ async afterAllProjectsVersioned(rootVersionActionsOptions) {
437
+ const changedFiles = new Set();
438
+ const deletedFiles = new Set();
439
+ for (const [, afterAllProjectsVersioned] of this
440
+ .uniqueAfterAllProjectsVersioned) {
441
+ const { changedFiles: changedFilesForVersionActions, deletedFiles: deletedFilesForVersionActions, } = await afterAllProjectsVersioned(this.tree.root, {
442
+ dryRun: this.options.dryRun,
443
+ verbose: this.options.verbose,
444
+ rootVersionActionsOptions,
445
+ });
446
+ for (const file of changedFilesForVersionActions) {
447
+ changedFiles.add(file);
448
+ }
449
+ for (const file of deletedFilesForVersionActions) {
450
+ deletedFiles.add(file);
451
+ }
452
+ }
453
+ return {
454
+ changedFiles: Array.from(changedFiles),
455
+ deletedFiles: Array.from(deletedFiles),
456
+ };
457
+ }
458
+ async processGroup(releaseGroupName) {
459
+ const groupNode = this.groupGraph.get(releaseGroupName);
460
+ const bumped = await this.bumpVersions(groupNode.group);
461
+ // Flush the project loggers for the group
462
+ for (const project of groupNode.group.projects) {
463
+ const projectLogger = this.getProjectLoggerForProject(project);
464
+ projectLogger.flush();
465
+ }
466
+ if (bumped) {
467
+ await this.propagateChangesToDependentGroups(releaseGroupName);
468
+ }
469
+ }
470
+ async propagateChangesToDependentGroups(changedReleaseGroupName) {
471
+ const changedGroupNode = this.groupGraph.get(changedReleaseGroupName);
472
+ for (const depGroupName of changedGroupNode.dependents) {
473
+ if (!this.processedGroups.has(depGroupName)) {
474
+ await this.propagateChanges(depGroupName, changedReleaseGroupName);
475
+ }
476
+ }
477
+ }
478
+ async bumpVersions(releaseGroup) {
479
+ if (releaseGroup.projectsRelationship === 'fixed') {
480
+ return this.bumpFixedVersionGroup(releaseGroup);
481
+ }
482
+ else {
483
+ return this.bumpIndependentVersionGroup(releaseGroup);
484
+ }
485
+ }
486
+ async bumpFixedVersionGroup(releaseGroup) {
487
+ if (releaseGroup.projects.length === 0) {
488
+ return false;
489
+ }
490
+ let bumped = false;
491
+ const firstProject = releaseGroup.projects[0];
492
+ const { newVersionInput, newVersionInputReason, newVersionInputReasonData, } = await this.determineVersionBumpForProject(releaseGroup, firstProject);
493
+ if (newVersionInput === 'none') {
494
+ // No direct bump for this group, but we may still need to bump if a dependency group has been bumped
495
+ let bumpedByDependency = false;
496
+ // Use sorted projects to check for dependencies in processed groups
497
+ const sortedProjects = this.sortedProjects.get(releaseGroup.name) || [];
498
+ // Iterate through each project in the release group in topological order
499
+ for (const project of sortedProjects) {
500
+ const dependencies = this.projectGraph.dependencies[project] || [];
501
+ for (const dep of dependencies) {
502
+ const depGroup = this.getReleaseGroupNameForProject(dep.target);
503
+ if (depGroup &&
504
+ depGroup !== releaseGroup.name &&
505
+ this.processedGroups.has(depGroup)) {
506
+ const depGroupBumpType = await this.getFixedReleaseGroupBumpType(depGroup);
507
+ // If a dependency group has been bumped, determine if it should trigger a bump in this group
508
+ if (depGroupBumpType !== 'none') {
509
+ bumpedByDependency = true;
510
+ const depBumpType = this.determineSideEffectBump(releaseGroup, depGroupBumpType);
511
+ await this.bumpVersionForProject(project, depBumpType, 'DEPENDENCY_ACROSS_GROUPS_WAS_BUMPED', {});
512
+ this.bumpedProjects.add(project);
513
+ // Update any dependencies in the manifest
514
+ await this.updateDependenciesForProject(project);
515
+ }
516
+ }
517
+ }
518
+ }
519
+ // If any project in the group was bumped due to dependency changes, we must bump all projects in the fixed group
520
+ if (bumpedByDependency) {
521
+ // Update all projects in topological order
522
+ for (const project of sortedProjects) {
523
+ if (!this.bumpedProjects.has(project)) {
524
+ await this.bumpVersionForProject(project, 'patch', 'OTHER_PROJECT_IN_FIXED_GROUP_WAS_BUMPED_DUE_TO_DEPENDENCY', {});
525
+ // Ensure the bump for remaining projects
526
+ this.bumpedProjects.add(project);
527
+ await this.updateDependenciesForProject(project);
528
+ }
529
+ }
530
+ }
531
+ else {
532
+ /**
533
+ * No projects in the group are being bumped, but as it stands only the first project would have an appropriate log,
534
+ * therefore add in an extra log for each additional project in the group, and we also need to make sure that the
535
+ * versionData is fully populated.
536
+ */
537
+ for (const project of releaseGroup.projects) {
538
+ this.versionData.set(project, {
539
+ currentVersion: this.getCurrentCachedVersionForProject(project),
540
+ newVersion: null,
541
+ dependentProjects: this.getOriginalDependentProjects(project),
542
+ });
543
+ if (project === firstProject) {
544
+ continue;
545
+ }
546
+ const projectLogger = this.getProjectLoggerForProject(project);
547
+ projectLogger.buffer(`🚫 Skipping versioning for ${project} as it is a part of a fixed release group with ${firstProject} and no dependency bumps were detected`);
548
+ }
549
+ }
550
+ return bumpedByDependency;
551
+ }
552
+ const { newVersion } = await this.calculateNewVersion(firstProject, newVersionInput, newVersionInputReason, newVersionInputReasonData);
553
+ // Use sorted projects for processing projects in the right order
554
+ const sortedProjects = this.sortedProjects.get(releaseGroup.name) || releaseGroup.projects;
555
+ // First, update versions for all projects in the fixed group in topological order
556
+ for (const project of sortedProjects) {
557
+ const versionActions = this.getVersionActionsForProject(project);
558
+ const projectLogger = this.getProjectLoggerForProject(project);
559
+ const currentVersion = this.getCurrentCachedVersionForProject(project);
560
+ // The first project's version was determined above, so this log is only appropriate for the remaining projects
561
+ if (project !== firstProject) {
562
+ projectLogger.buffer(`❓ Applied version ${newVersion} directly, because the project is a member of a fixed release group containing ${firstProject}`);
563
+ }
564
+ /**
565
+ * Update the project's version based on the implementation details of the configured VersionActions
566
+ * and display any returned log messages to the user.
567
+ */
568
+ const logMessages = await versionActions.updateProjectVersion(this.tree, newVersion);
569
+ for (const logMessage of logMessages) {
570
+ projectLogger.buffer(logMessage);
571
+ }
572
+ this.bumpedProjects.add(project);
573
+ bumped = true;
574
+ // Populate version data for each project
575
+ this.versionData.set(project, {
576
+ currentVersion,
577
+ newVersion,
578
+ dependentProjects: this.getOriginalDependentProjects(project),
579
+ });
580
+ }
581
+ // Then, update dependencies for all projects in the fixed group, also in topological order
582
+ if (bumped) {
583
+ for (const project of sortedProjects) {
584
+ await this.updateDependenciesForProject(project);
585
+ }
586
+ }
587
+ return bumped;
588
+ }
589
+ async bumpIndependentVersionGroup(releaseGroup) {
590
+ const releaseGroupFilteredProjects = this.releaseGroupToFilteredProjects.get(releaseGroup);
591
+ let bumped = false;
592
+ const projectBumpTypes = new Map();
593
+ const projectsToUpdate = new Set();
594
+ // First pass: Determine bump types
595
+ for (const project of this.allProjectsToProcess) {
596
+ const { newVersionInput: bumpType, newVersionInputReason: bumpTypeReason, newVersionInputReasonData: bumpTypeReasonData, } = await this.determineVersionBumpForProject(releaseGroup, project);
597
+ projectBumpTypes.set(project, {
598
+ bumpType,
599
+ bumpTypeReason,
600
+ bumpTypeReasonData,
601
+ });
602
+ if (bumpType !== 'none') {
603
+ projectsToUpdate.add(project);
604
+ }
605
+ }
606
+ // Second pass: Update versions using topologically sorted projects
607
+ // This ensures dependencies are processed before dependents
608
+ const sortedProjects = this.sortedProjects.get(releaseGroup.name) || [];
609
+ // Process projects in topological order
610
+ for (const project of sortedProjects) {
611
+ if (projectsToUpdate.has(project) &&
612
+ releaseGroupFilteredProjects.has(project)) {
613
+ const { bumpType: finalBumpType, bumpTypeReason: finalBumpTypeReason, bumpTypeReasonData: finalBumpTypeReasonData, } = projectBumpTypes.get(project);
614
+ if (finalBumpType !== 'none') {
615
+ await this.bumpVersionForProject(project, finalBumpType, finalBumpTypeReason, finalBumpTypeReasonData);
616
+ this.bumpedProjects.add(project);
617
+ bumped = true;
618
+ }
619
+ }
620
+ }
621
+ // Third pass: Update dependencies also in topological order
622
+ for (const project of sortedProjects) {
623
+ if (projectsToUpdate.has(project) &&
624
+ releaseGroupFilteredProjects.has(project)) {
625
+ await this.updateDependenciesForProject(project);
626
+ }
627
+ }
628
+ return bumped;
629
+ }
630
+ async determineVersionBumpForProject(releaseGroup, projectName) {
631
+ // User given specifier has the highest precedence
632
+ if (this.userGivenSpecifier) {
633
+ return {
634
+ newVersionInput: this.userGivenSpecifier,
635
+ newVersionInputReason: 'USER_SPECIFIER',
636
+ newVersionInputReasonData: {},
637
+ };
638
+ }
639
+ const projectGraphNode = this.projectGraph.nodes[projectName];
640
+ const projectLogger = this.getProjectLoggerForProject(projectName);
641
+ const cachedFinalConfigForProject = this.getCachedFinalConfigForProject(projectName);
642
+ if (cachedFinalConfigForProject.specifierSource === 'conventional-commits') {
643
+ const currentVersion = this.getCurrentCachedVersionForProject(projectName);
644
+ const bumpType = await (0, derive_specifier_from_conventional_commits_1.deriveSpecifierFromConventionalCommits)(this.nxReleaseConfig, this.projectGraph, projectLogger, releaseGroup, projectGraphNode, !!semver.prerelease(currentVersion ?? ''), this.cachedLatestMatchingGitTag.get(projectName), cachedFinalConfigForProject.fallbackCurrentVersionResolver, this.options.preid);
645
+ return {
646
+ newVersionInput: bumpType,
647
+ newVersionInputReason: 'CONVENTIONAL_COMMITS',
648
+ newVersionInputReasonData: {},
649
+ };
650
+ }
651
+ // Resolve the semver relative bump via version-plans
652
+ if (releaseGroup.versionPlans) {
653
+ const currentVersion = this.getCurrentCachedVersionForProject(projectName);
654
+ const { bumpType, versionPlanPath } = await (0, deriver_specifier_from_version_plans_1.deriveSpecifierFromVersionPlan)(projectLogger, releaseGroup, projectGraphNode, currentVersion);
655
+ if (bumpType !== 'none') {
656
+ this.processedVersionPlanFiles.add(versionPlanPath);
657
+ }
658
+ return {
659
+ newVersionInput: bumpType,
660
+ newVersionInputReason: 'VERSION_PLANS',
661
+ newVersionInputReasonData: {
662
+ versionPlanPath,
663
+ },
664
+ };
665
+ }
666
+ // Only add the release group name to the log if it is one set by the user, otherwise it is useless noise
667
+ const maybeLogReleaseGroup = (log) => {
668
+ if (releaseGroup.name === config_1.IMPLICIT_DEFAULT_RELEASE_GROUP) {
669
+ return log;
670
+ }
671
+ return `${log} within release group "${releaseGroup.name}"`;
672
+ };
673
+ if (cachedFinalConfigForProject.specifierSource === 'prompt') {
674
+ let specifier;
675
+ if (releaseGroup.projectsRelationship === 'independent') {
676
+ specifier = await (0, resolve_semver_specifier_1.resolveSemverSpecifierFromPrompt)(`${maybeLogReleaseGroup(`What kind of change is this for project "${projectName}"`)}?`, `${maybeLogReleaseGroup(`What is the exact version for project "${projectName}"`)}?`);
677
+ }
678
+ else {
679
+ specifier = await (0, resolve_semver_specifier_1.resolveSemverSpecifierFromPrompt)(`${maybeLogReleaseGroup(`What kind of change is this for the ${releaseGroup.projects.length} matched projects(s)`)}?`, `${maybeLogReleaseGroup(`What is the exact version for the ${releaseGroup.projects.length} matched project(s)`)}?`);
680
+ }
681
+ return {
682
+ newVersionInput: specifier,
683
+ newVersionInputReason: 'PROMPTED_USER_SPECIFIER',
684
+ newVersionInputReasonData: {},
685
+ };
686
+ }
687
+ throw new Error(`Unhandled version bump config, please report this as a bug on https://github.com/nrwl/nx/issues`);
688
+ }
689
+ getVersionActionsForProject(projectName) {
690
+ const versionActions = this.projectsToVersionActions.get(projectName);
691
+ if (!versionActions) {
692
+ throw new Error(`No versionActions found for project ${projectName}, please report this as a bug on https://github.com/nrwl/nx/issues`);
693
+ }
694
+ return versionActions;
695
+ }
696
+ getFinalConfigForProject(projectName) {
697
+ const finalConfig = this.finalConfigsByProject.get(projectName);
698
+ if (!finalConfig) {
699
+ throw new Error(`No final config found for project ${projectName}, please report this as a bug on https://github.com/nrwl/nx/issues`);
700
+ }
701
+ return finalConfig;
702
+ }
703
+ getProjectLoggerForProject(projectName) {
704
+ const projectLogger = this.projectLoggers.get(projectName);
705
+ if (!projectLogger) {
706
+ throw new Error(`No project logger found for project ${projectName}, please report this as a bug on https://github.com/nrwl/nx/issues`);
707
+ }
708
+ return projectLogger;
709
+ }
710
+ getCurrentCachedVersionForProject(projectName) {
711
+ return this.cachedCurrentVersions.get(projectName);
712
+ }
713
+ getCachedFinalConfigForProject(projectName) {
714
+ const cachedFinalConfig = this.finalConfigsByProject.get(projectName);
715
+ if (!cachedFinalConfig) {
716
+ throw new Error(`Unexpected error: No cached config found for project ${projectName}, please report this as a bug on https://github.com/nrwl/nx/issues`);
717
+ }
718
+ return cachedFinalConfig;
719
+ }
720
+ /**
721
+ * Apply project and release group precedence and default values, as well as validate the final configuration,
722
+ * ready to be cached.
723
+ */
724
+ resolveFinalConfigForProject(releaseGroup, projectGraphNode) {
725
+ const releaseGroupVersionConfig = releaseGroup.version;
726
+ const projectVersionConfig = projectGraphNode.data.release?.version;
727
+ /**
728
+ * specifierSource
729
+ *
730
+ * If the user has provided a specifier, it always takes precedence,
731
+ * so the effective specifier source is 'prompt', regardless of what
732
+ * the project or release group config says.
733
+ */
734
+ const specifierSource = this.userGivenSpecifier
735
+ ? 'prompt'
736
+ : projectVersionConfig?.specifierSource ??
737
+ releaseGroupVersionConfig?.specifierSource ??
738
+ 'prompt';
739
+ /**
740
+ * versionPrefix, defaults to auto
741
+ */
742
+ const versionPrefix = projectVersionConfig?.versionPrefix ??
743
+ releaseGroupVersionConfig?.versionPrefix ??
744
+ 'auto';
745
+ if (versionPrefix && !version_1.validReleaseVersionPrefixes.includes(versionPrefix)) {
746
+ throw new Error(`Invalid value for versionPrefix: "${versionPrefix}"
747
+
748
+ Valid values are: ${version_1.validReleaseVersionPrefixes
749
+ .map((s) => `"${s}"`)
750
+ .join(', ')}`);
751
+ }
752
+ /**
753
+ * currentVersionResolver, defaults to disk
754
+ */
755
+ const currentVersionResolver = projectVersionConfig?.currentVersionResolver ??
756
+ releaseGroupVersionConfig?.currentVersionResolver ??
757
+ 'disk';
758
+ if (specifierSource === 'conventional-commits' &&
759
+ currentVersionResolver !== 'git-tag') {
760
+ throw new Error(`Invalid currentVersionResolver "${currentVersionResolver}" provided for project "${projectGraphNode.name}". Must be "git-tag" when "specifierSource" is "conventional-commits"`);
761
+ }
762
+ /**
763
+ * currentVersionResolverMetadata, defaults to {}
764
+ */
765
+ const currentVersionResolverMetadata = projectVersionConfig?.currentVersionResolverMetadata ??
766
+ releaseGroupVersionConfig?.currentVersionResolverMetadata ??
767
+ {};
768
+ /**
769
+ * preserveLocalDependencyProtocols
770
+ *
771
+ * This was false by default in legacy versioning, but is true by default now.
772
+ */
773
+ const preserveLocalDependencyProtocols = projectVersionConfig?.preserveLocalDependencyProtocols ??
774
+ releaseGroupVersionConfig?.preserveLocalDependencyProtocols ??
775
+ true;
776
+ /**
777
+ * fallbackCurrentVersionResolver, defaults to disk when performing a first release, otherwise undefined
778
+ */
779
+ const fallbackCurrentVersionResolver = projectVersionConfig?.fallbackCurrentVersionResolver ??
780
+ releaseGroupVersionConfig?.fallbackCurrentVersionResolver ??
781
+ // Always fall back to disk if this is the first release
782
+ (this.options.firstRelease ? 'disk' : undefined);
783
+ /**
784
+ * versionActionsOptions, defaults to {}
785
+ */
786
+ let versionActionsOptions = projectVersionConfig?.versionActionsOptions ??
787
+ releaseGroupVersionConfig?.versionActionsOptions ??
788
+ {};
789
+ // Apply any optional overrides that may be passed in from the programmatic API
790
+ versionActionsOptions = {
791
+ ...versionActionsOptions,
792
+ ...(this.options.versionActionsOptionsOverrides ?? {}),
793
+ };
794
+ const manifestRootsToUpdate = (projectVersionConfig?.manifestRootsToUpdate ??
795
+ releaseGroupVersionConfig?.manifestRootsToUpdate ??
796
+ []).map((manifestRoot) => {
797
+ if (typeof manifestRoot === 'string') {
798
+ return {
799
+ path: manifestRoot,
800
+ // Apply the project level preserveLocalDependencyProtocols setting that was already resolved
801
+ preserveLocalDependencyProtocols,
802
+ };
803
+ }
804
+ return manifestRoot;
805
+ });
806
+ return {
807
+ specifierSource,
808
+ currentVersionResolver,
809
+ currentVersionResolverMetadata,
810
+ fallbackCurrentVersionResolver,
811
+ versionPrefix,
812
+ preserveLocalDependencyProtocols,
813
+ versionActionsOptions,
814
+ manifestRootsToUpdate,
815
+ };
816
+ }
817
+ async calculateNewVersion(project, newVersionInput, // any arbitrary string, whether or not it is valid is dependent upon the version actions implementation
818
+ newVersionInputReason, newVersionInputReasonData) {
819
+ const currentVersion = this.getCurrentCachedVersionForProject(project);
820
+ const versionActions = this.getVersionActionsForProject(project);
821
+ const { newVersion, logText } = await versionActions.calculateNewVersion(currentVersion, newVersionInput, newVersionInputReason, newVersionInputReasonData, this.options.preid);
822
+ const projectLogger = this.getProjectLoggerForProject(project);
823
+ projectLogger.buffer(logText);
824
+ return { currentVersion, newVersion };
825
+ }
826
+ async updateDependenciesForProject(projectName) {
827
+ if (!this.allProjectsToProcess.has(projectName)) {
828
+ throw new Error(`Unable to find ${projectName} in allProjectsToProcess, please report this as a bug on https://github.com/nrwl/nx/issues`);
829
+ }
830
+ const versionActions = this.getVersionActionsForProject(projectName);
831
+ const cachedFinalConfigForProject = this.getCachedFinalConfigForProject(projectName);
832
+ const dependenciesToUpdate = {};
833
+ const dependencies = this.projectGraph.dependencies[projectName] || [];
834
+ for (const dep of dependencies) {
835
+ if (this.allProjectsToProcess.has(dep.target) &&
836
+ this.bumpedProjects.has(dep.target)) {
837
+ const targetVersionData = this.versionData.get(dep.target);
838
+ if (targetVersionData) {
839
+ const { currentVersion: currentDependencyVersion } = await versionActions.readCurrentVersionOfDependency(this.tree, this.projectGraph, dep.target);
840
+ if (!currentDependencyVersion) {
841
+ continue;
842
+ }
843
+ let finalPrefix = '';
844
+ if (cachedFinalConfigForProject.versionPrefix === 'auto') {
845
+ const prefixMatch = currentDependencyVersion?.match(/^([~^=])/);
846
+ finalPrefix = prefixMatch ? prefixMatch[1] : '';
847
+ }
848
+ else if (['~', '^', '='].includes(cachedFinalConfigForProject.versionPrefix)) {
849
+ finalPrefix = cachedFinalConfigForProject.versionPrefix;
850
+ }
851
+ // Remove any existing prefix from the new version before applying the finalPrefix
852
+ const cleanNewVersion = targetVersionData.newVersion.replace(/^[~^=]/, '');
853
+ dependenciesToUpdate[dep.target] = `${finalPrefix}${cleanNewVersion}`;
854
+ }
855
+ }
856
+ }
857
+ const projectLogger = this.getProjectLoggerForProject(projectName);
858
+ const logMessages = await versionActions.updateProjectDependencies(this.tree, this.projectGraph, dependenciesToUpdate);
859
+ for (const logMessage of logMessages) {
860
+ projectLogger.buffer(logMessage);
861
+ }
862
+ }
863
+ async bumpVersionForProject(projectName, bumpType, bumpTypeReason, bumpTypeReasonData) {
864
+ const projectLogger = this.getProjectLoggerForProject(projectName);
865
+ if (bumpType === 'none') {
866
+ projectLogger.buffer(`⏩ Skipping bump for ${projectName} as bump type is "none"`);
867
+ return;
868
+ }
869
+ const versionActions = this.getVersionActionsForProject(projectName);
870
+ const { currentVersion, newVersion } = await this.calculateNewVersion(projectName, bumpType, bumpTypeReason, bumpTypeReasonData);
871
+ /**
872
+ * Update the project's version based on the implementation details of the configured VersionActions
873
+ * and display any returned log messages to the user.
874
+ */
875
+ const logMessages = await versionActions.updateProjectVersion(this.tree, newVersion);
876
+ for (const logMessage of logMessages) {
877
+ projectLogger.buffer(logMessage);
878
+ }
879
+ // Update version data and bumped projects
880
+ this.versionData.set(projectName, {
881
+ currentVersion,
882
+ newVersion,
883
+ dependentProjects: this.getOriginalDependentProjects(projectName),
884
+ });
885
+ this.bumpedProjects.add(projectName);
886
+ // Find the release group for this project
887
+ const releaseGroupName = this.getReleaseGroupNameForProject(projectName);
888
+ if (!releaseGroupName) {
889
+ projectLogger.buffer(`⚠️ Cannot find release group for ${projectName}, skipping dependent updates`);
890
+ return;
891
+ }
892
+ const releaseGroup = this.groupGraph.get(releaseGroupName).group;
893
+ const releaseGroupVersionConfig = releaseGroup.version;
894
+ // Get updateDependents from the release group level config
895
+ const updateDependents = releaseGroupVersionConfig?.updateDependents ||
896
+ 'auto';
897
+ // Only update dependencies for dependents if the group's updateDependents is 'auto'
898
+ if (updateDependents === 'auto') {
899
+ const dependents = this.getNonImplicitDependentsForProject(projectName);
900
+ await this.updateDependenciesForDependents(dependents);
901
+ for (const dependent of dependents) {
902
+ if (this.allProjectsToProcess.has(dependent) &&
903
+ !this.bumpedProjects.has(dependent)) {
904
+ await this.bumpVersionForProject(dependent, 'patch', 'DEPENDENCY_WAS_BUMPED', {});
905
+ }
906
+ }
907
+ }
908
+ else {
909
+ const releaseGroupText = releaseGroupName !== config_1.IMPLICIT_DEFAULT_RELEASE_GROUP
910
+ ? ` in release group "${releaseGroupName}" `
911
+ : ' ';
912
+ projectLogger.buffer(`⏩ Skipping dependent updates as "updateDependents"${releaseGroupText}is not "auto"`);
913
+ }
914
+ }
915
+ async updateDependenciesForDependents(dependents) {
916
+ for (const dependent of dependents) {
917
+ if (!this.allProjectsToProcess.has(dependent)) {
918
+ throw new Error(`Unable to find project "${dependent}" in allProjectsToProcess, please report this as a bug on https://github.com/nrwl/nx/issues`);
919
+ }
920
+ await this.updateDependenciesForProject(dependent);
921
+ }
922
+ }
923
+ getOriginalDependentProjects(project) {
924
+ return this.originalDependentProjectsPerProject.get(project) || [];
925
+ }
926
+ async propagateChanges(releaseGroupName, changedDependencyGroup) {
927
+ const releaseGroup = this.groupGraph.get(releaseGroupName).group;
928
+ const releaseGroupFilteredProjects = this.releaseGroupToFilteredProjects.get(releaseGroup);
929
+ // Get updateDependents from the release group level config
930
+ const releaseGroupVersionConfig = releaseGroup.version;
931
+ const updateDependents = releaseGroupVersionConfig?.updateDependents ||
932
+ 'auto';
933
+ // If updateDependents is not 'auto', skip propagating changes to this group
934
+ if (updateDependents !== 'auto') {
935
+ const projectLogger = this.getProjectLoggerForProject(releaseGroupFilteredProjects.values().next().value);
936
+ projectLogger.buffer(`⏩ Skipping dependency updates for release group "${releaseGroupName}" as "updateDependents" is not "auto"`);
937
+ return;
938
+ }
939
+ let groupBumped = false;
940
+ let bumpType = 'none';
941
+ if (releaseGroup.projectsRelationship === 'fixed') {
942
+ // For fixed groups, we only need to check one project
943
+ const project = releaseGroupFilteredProjects[0];
944
+ const dependencies = this.projectGraph.dependencies[project] || [];
945
+ const hasDependencyInChangedGroup = dependencies.some((dep) => this.getReleaseGroupNameForProject(dep.target) ===
946
+ changedDependencyGroup);
947
+ if (hasDependencyInChangedGroup) {
948
+ const dependencyBumpType = await this.getFixedReleaseGroupBumpType(changedDependencyGroup);
949
+ bumpType = this.determineSideEffectBump(releaseGroup, dependencyBumpType);
950
+ groupBumped = bumpType !== 'none';
951
+ }
952
+ }
953
+ if (groupBumped) {
954
+ for (const project of releaseGroupFilteredProjects) {
955
+ if (!this.bumpedProjects.has(project)) {
956
+ await this.bumpVersionForProject(project, bumpType, 'DEPENDENCY_ACROSS_GROUPS_WAS_BUMPED', {});
957
+ this.bumpedProjects.add(project);
958
+ }
959
+ }
960
+ }
961
+ }
962
+ async getFixedReleaseGroupBumpType(releaseGroupName) {
963
+ const releaseGroup = this.groupGraph.get(releaseGroupName).group;
964
+ const releaseGroupFilteredProjects = this.releaseGroupToFilteredProjects.get(releaseGroup);
965
+ if (releaseGroupFilteredProjects.size === 0) {
966
+ return 'none';
967
+ }
968
+ const { newVersionInput } = await this.determineVersionBumpForProject(releaseGroup,
969
+ // It's a fixed release group, so we can just pick any project in the group
970
+ releaseGroupFilteredProjects.values().next().value);
971
+ return newVersionInput;
972
+ }
973
+ // TODO: Support influencing the side effect bump in a future version, always patch for now like in legacy versioning
974
+ determineSideEffectBump(releaseGroup, dependencyBumpType) {
975
+ const sideEffectBump = 'patch';
976
+ return sideEffectBump;
977
+ }
978
+ getProjectDependents(project) {
979
+ return this.projectToDependents.get(project) || new Set();
980
+ }
981
+ getAllNonImplicitDependents(projects) {
982
+ return projects
983
+ .flatMap((project) => {
984
+ const dependentProjectNames = this.getNonImplicitDependentsForProject(project);
985
+ this.tmpCachedDependentProjects.set(project, dependentProjectNames);
986
+ return dependentProjectNames;
987
+ })
988
+ .filter((dep) => !this.allProjectsToProcess.has(dep));
989
+ }
990
+ getNonImplicitDependentsForProject(project) {
991
+ // Use the cached dependents for O(1) lookup instead of O(n) scan
992
+ return Array.from(this.getProjectDependents(project));
993
+ }
994
+ hasAutoUpdateDependents(projectName) {
995
+ return this.projectToUpdateDependentsSetting.get(projectName) === 'auto';
996
+ }
997
+ topologicallySortReleaseGroups() {
998
+ // Get all release group names
999
+ const groupNames = Array.from(this.groupGraph.keys());
1000
+ // Function to get dependencies of a group
1001
+ const getGroupDependencies = (groupName) => {
1002
+ const groupNode = this.groupGraph.get(groupName);
1003
+ if (!groupNode) {
1004
+ return [];
1005
+ }
1006
+ return Array.from(groupNode.dependencies);
1007
+ };
1008
+ // Perform topological sort
1009
+ return (0, topological_sort_1.topologicalSort)(groupNames, getGroupDependencies);
1010
+ }
1011
+ topologicallySortProjects(releaseGroup) {
1012
+ // For fixed relationship groups, the order doesn't matter since all projects will
1013
+ // be versioned identically, but we still sort them for consistency
1014
+ const projects = releaseGroup.projects.filter((p) =>
1015
+ // Only include projects that are in allProjectsToProcess
1016
+ this.allProjectsToProcess.has(p));
1017
+ // Function to get dependencies of a project that are in the same release group
1018
+ const getProjectDependenciesInSameGroup = (project) => {
1019
+ const deps = this.getProjectDependencies(project);
1020
+ // Only include dependencies that are in the same release group and in allProjectsToProcess
1021
+ return Array.from(deps).filter((dep) => this.getReleaseGroupNameForProject(dep) === releaseGroup.name &&
1022
+ this.allProjectsToProcess.has(dep));
1023
+ };
1024
+ // Perform topological sort
1025
+ return (0, topological_sort_1.topologicalSort)(projects, getProjectDependenciesInSameGroup);
1026
+ }
1027
+ /**
1028
+ * Precompute project -> dependents/dependencies relationships for O(1) lookups
1029
+ */
1030
+ async precomputeDependencyRelationships() {
1031
+ for (const projectName of this.allProjectsConfiguredForNxRelease) {
1032
+ const versionActions = this.projectsToVersionActions.get(projectName);
1033
+ // Create a new set for this project's dependencies
1034
+ if (!this.projectToDependencies.has(projectName)) {
1035
+ this.projectToDependencies.set(projectName, new Set());
1036
+ }
1037
+ const deps = await versionActions.readDependencies(this.tree, this.projectGraph);
1038
+ for (const dep of deps) {
1039
+ // Skip dependencies not covered by nx release
1040
+ if (!this.allProjectsConfiguredForNxRelease.has(dep.target)) {
1041
+ continue;
1042
+ }
1043
+ // Add this dependency to the project's dependencies
1044
+ this.projectToDependencies.get(projectName).add(dep.target);
1045
+ // Add this project as a dependent of the target
1046
+ if (!this.projectToDependents.has(dep.target)) {
1047
+ this.projectToDependents.set(dep.target, new Set());
1048
+ }
1049
+ this.projectToDependents.get(dep.target).add(projectName);
1050
+ }
1051
+ }
1052
+ }
1053
+ getProjectDependencies(project) {
1054
+ return this.projectToDependencies.get(project) || new Set();
1055
+ }
1056
+ }
1057
+ exports.ReleaseGroupProcessor = ReleaseGroupProcessor;