nx 21.0.0-beta.1 → 21.0.0-beta.3

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