nx 17.0.3 → 17.0.5

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 (320) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +9 -4
  3. package/bin/init-local.js +10 -60
  4. package/bin/nx-cloud.js +6 -0
  5. package/bin/nx.js +1 -9
  6. package/bin/post-install.js +5 -1
  7. package/bin/run-executor.js +1 -1
  8. package/migrations.json +18 -0
  9. package/package.json +21 -18
  10. package/plugins/package-json.js +1 -1
  11. package/{changelog-renderer → release/changelog-renderer}/index.d.ts +18 -3
  12. package/{changelog-renderer → release/changelog-renderer}/index.js +78 -34
  13. package/release/index.d.ts +4 -0
  14. package/release/index.js +11 -0
  15. package/schemas/nx-schema.json +203 -19
  16. package/schemas/project-schema.json +20 -0
  17. package/src/adapter/angular-json.js +28 -18
  18. package/src/adapter/compat.d.ts +2 -2
  19. package/src/adapter/compat.js +6 -1
  20. package/src/adapter/ngcli-adapter.d.ts +16 -4
  21. package/src/adapter/ngcli-adapter.js +85 -28
  22. package/src/command-line/add/add.d.ts +2 -0
  23. package/src/command-line/add/add.js +169 -0
  24. package/src/command-line/add/command-object.d.ts +7 -0
  25. package/src/command-line/add/command-object.js +24 -0
  26. package/src/command-line/affected/affected.js +0 -3
  27. package/src/command-line/affected/command-object.d.ts +4 -4
  28. package/src/command-line/affected/command-object.js +53 -26
  29. package/src/command-line/affected/print-affected.js +10 -1
  30. package/src/command-line/connect/command-object.d.ts +1 -2
  31. package/src/command-line/connect/command-object.js +3 -7
  32. package/src/command-line/connect/connect-to-nx-cloud.d.ts +4 -5
  33. package/src/command-line/connect/connect-to-nx-cloud.js +43 -31
  34. package/src/command-line/connect/view-logs.js +5 -21
  35. package/src/command-line/examples.js +18 -0
  36. package/src/command-line/exec/command-object.js +2 -1
  37. package/src/command-line/exec/exec.d.ts +1 -1
  38. package/src/command-line/exec/exec.js +78 -35
  39. package/src/command-line/format/format.js +13 -4
  40. package/src/command-line/generate/generate.js +9 -9
  41. package/src/command-line/generate/generator-utils.d.ts +3 -2
  42. package/src/command-line/generate/generator-utils.js +5 -5
  43. package/src/command-line/graph/graph.d.ts +1 -1
  44. package/src/command-line/graph/graph.js +82 -37
  45. package/src/command-line/init/command-object.js +70 -43
  46. package/src/command-line/init/implementation/add-nx-to-monorepo.d.ts +4 -2
  47. package/src/command-line/init/implementation/add-nx-to-monorepo.js +11 -2
  48. package/src/command-line/init/implementation/add-nx-to-nest.d.ts +1 -1
  49. package/src/command-line/init/implementation/add-nx-to-nest.js +9 -6
  50. package/src/command-line/init/implementation/add-nx-to-npm-repo.d.ts +4 -2
  51. package/src/command-line/init/implementation/add-nx-to-npm-repo.js +14 -4
  52. package/src/command-line/init/implementation/angular/index.js +4 -1
  53. package/src/command-line/init/implementation/angular/legacy-angular-versions.js +10 -5
  54. package/src/command-line/init/implementation/angular/standalone-workspace.js +1 -1
  55. package/src/command-line/init/implementation/angular/types.d.ts +1 -1
  56. package/src/command-line/init/implementation/dot-nx/nxw.js +47 -16
  57. package/src/command-line/init/implementation/react/index.d.ts +1 -1
  58. package/src/command-line/init/implementation/react/index.js +4 -2
  59. package/src/command-line/init/implementation/react/rename-js-to-jsx.js +4 -2
  60. package/src/command-line/init/implementation/utils.d.ts +5 -5
  61. package/src/command-line/init/implementation/utils.js +46 -44
  62. package/src/command-line/init/{init.js → init-v1.js} +5 -13
  63. package/src/command-line/init/init-v2.d.ts +7 -0
  64. package/src/command-line/init/init-v2.js +201 -0
  65. package/src/command-line/list/list.js +6 -5
  66. package/src/command-line/migrate/command-object.js +19 -4
  67. package/src/command-line/migrate/migrate.js +21 -17
  68. package/src/command-line/new/new.js +1 -1
  69. package/src/command-line/nx-commands.js +4 -1
  70. package/src/command-line/release/changelog.d.ts +22 -1
  71. package/src/command-line/release/changelog.js +459 -257
  72. package/src/command-line/release/command-object.d.ts +29 -7
  73. package/src/command-line/release/command-object.js +120 -20
  74. package/src/command-line/release/config/config.d.ts +26 -21
  75. package/src/command-line/release/config/config.js +416 -59
  76. package/src/command-line/release/config/conventional-commits.d.ts +2 -0
  77. package/src/command-line/release/config/conventional-commits.js +98 -0
  78. package/src/command-line/release/config/filter-release-groups.d.ts +1 -2
  79. package/src/command-line/release/config/filter-release-groups.js +38 -1
  80. package/src/command-line/release/index.d.ts +16 -0
  81. package/src/command-line/release/index.js +23 -0
  82. package/src/command-line/release/publish.d.ts +7 -3
  83. package/src/command-line/release/publish.js +89 -37
  84. package/src/command-line/release/release.d.ts +4 -0
  85. package/src/command-line/release/release.js +176 -0
  86. package/src/command-line/release/utils/batch-projects-by-generator-config.d.ts +7 -0
  87. package/src/command-line/release/utils/batch-projects-by-generator-config.js +37 -0
  88. package/src/command-line/release/utils/exec-command.d.ts +1 -0
  89. package/src/command-line/release/utils/exec-command.js +34 -0
  90. package/src/command-line/release/utils/git.d.ts +34 -1
  91. package/src/command-line/release/utils/git.js +238 -34
  92. package/src/command-line/release/utils/github.d.ts +5 -5
  93. package/src/command-line/release/utils/github.js +155 -8
  94. package/src/command-line/release/utils/markdown.js +6 -1
  95. package/src/command-line/release/utils/print-changes.d.ts +1 -1
  96. package/src/command-line/release/utils/print-changes.js +3 -3
  97. package/src/command-line/release/utils/resolve-nx-json-error-message.js +4 -1
  98. package/src/command-line/release/utils/resolve-semver-specifier.d.ts +4 -0
  99. package/src/command-line/release/utils/resolve-semver-specifier.js +58 -0
  100. package/src/command-line/release/utils/semver.d.ts +8 -0
  101. package/src/command-line/release/utils/semver.js +30 -1
  102. package/src/command-line/release/utils/shared.d.ts +39 -0
  103. package/src/command-line/release/utils/shared.js +213 -0
  104. package/src/command-line/release/version.d.ts +37 -3
  105. package/src/command-line/release/version.js +312 -117
  106. package/src/command-line/repair/repair.js +13 -9
  107. package/src/command-line/report/report.d.ts +3 -0
  108. package/src/command-line/report/report.js +22 -5
  109. package/src/command-line/run/command-object.d.ts +4 -0
  110. package/src/command-line/run/command-object.js +18 -2
  111. package/src/command-line/run/executor-utils.d.ts +2 -1
  112. package/src/command-line/run/executor-utils.js +4 -4
  113. package/src/command-line/run/run-one.js +3 -6
  114. package/src/command-line/run/run.js +34 -9
  115. package/src/command-line/run-many/command-object.js +4 -1
  116. package/src/command-line/run-many/run-many.js +0 -3
  117. package/src/command-line/show/command-object.d.ts +3 -0
  118. package/src/command-line/show/command-object.js +29 -2
  119. package/src/command-line/show/show.js +9 -0
  120. package/src/command-line/yargs-utils/shared-options.d.ts +4 -1
  121. package/src/command-line/yargs-utils/shared-options.js +23 -9
  122. package/src/commands-runner/command-graph.d.ts +13 -0
  123. package/src/commands-runner/command-graph.js +2 -0
  124. package/src/commands-runner/create-command-graph.d.ts +4 -0
  125. package/src/commands-runner/create-command-graph.js +44 -0
  126. package/src/commands-runner/get-command-projects.d.ts +3 -0
  127. package/src/commands-runner/get-command-projects.js +19 -0
  128. package/src/config/nx-json.d.ts +160 -25
  129. package/src/config/project-graph.d.ts +3 -3
  130. package/src/config/workspace-json-project-json.d.ts +23 -1
  131. package/src/config/workspaces.d.ts +1 -1
  132. package/src/config/workspaces.js +4 -6
  133. package/src/core/graph/3rdpartylicenses.txt +144 -74
  134. package/src/core/graph/environment.js +1 -1
  135. package/src/core/graph/index.html +4 -6
  136. package/src/core/graph/main.js +1 -1
  137. package/src/core/graph/runtime.js +1 -1
  138. package/src/core/graph/styles.css +3 -3
  139. package/src/core/graph/styles.js +1 -1
  140. package/src/daemon/client/client.d.ts +5 -1
  141. package/src/daemon/client/client.js +27 -8
  142. package/src/daemon/client/{socket-messenger.d.ts → daemon-socket-messenger.d.ts} +1 -1
  143. package/src/daemon/client/{socket-messenger.js → daemon-socket-messenger.js} +3 -3
  144. package/src/daemon/daemon-project-graph-error.d.ts +8 -0
  145. package/src/daemon/daemon-project-graph-error.js +13 -0
  146. package/src/daemon/server/handle-hash-tasks.js +12 -2
  147. package/src/daemon/server/handle-request-project-graph.js +1 -1
  148. package/src/daemon/server/project-graph-incremental-recomputation.d.ts +14 -13
  149. package/src/daemon/server/project-graph-incremental-recomputation.js +98 -33
  150. package/src/daemon/server/shutdown-utils.js +2 -4
  151. package/src/daemon/server/watcher.js +0 -3
  152. package/src/daemon/socket-utils.d.ts +2 -1
  153. package/src/daemon/socket-utils.js +15 -4
  154. package/src/daemon/tmp-dir.d.ts +1 -0
  155. package/src/daemon/tmp-dir.js +4 -4
  156. package/src/devkit-exports.d.ts +2 -2
  157. package/src/devkit-exports.js +3 -2
  158. package/src/devkit-internals.d.ts +3 -0
  159. package/src/devkit-internals.js +7 -1
  160. package/src/executors/noop/schema.json +1 -1
  161. package/src/executors/run-commands/run-commands.impl.d.ts +10 -2
  162. package/src/executors/run-commands/run-commands.impl.js +152 -48
  163. package/src/executors/run-commands/schema.json +11 -1
  164. package/src/executors/run-script/run-script.impl.js +43 -11
  165. package/src/executors/utils/convert-nx-executor.js +1 -1
  166. package/src/generators/internal-utils/format-changed-files-with-prettier-if-available.d.ts +3 -1
  167. package/src/generators/internal-utils/format-changed-files-with-prettier-if-available.js +4 -2
  168. package/src/generators/testing-utils/create-tree-with-empty-workspace.js +0 -6
  169. package/src/generators/tree.d.ts +1 -0
  170. package/src/generators/utils/glob.js +2 -2
  171. package/src/generators/utils/project-configuration.js +28 -9
  172. package/src/hasher/create-task-hasher.d.ts +4 -0
  173. package/src/hasher/create-task-hasher.js +16 -0
  174. package/src/hasher/hash-task.js +9 -3
  175. package/src/hasher/native-task-hasher-impl.d.ts +19 -0
  176. package/src/hasher/native-task-hasher-impl.js +37 -0
  177. package/src/hasher/node-task-hasher-impl.d.ts +49 -0
  178. package/src/hasher/node-task-hasher-impl.js +431 -0
  179. package/src/hasher/task-hasher.d.ts +33 -21
  180. package/src/hasher/task-hasher.js +30 -428
  181. package/src/migrations/update-15-0-0/migrate-to-inputs.js +5 -5
  182. package/src/migrations/update-15-1-0/set-project-names.js +2 -1
  183. package/src/migrations/update-15-8-2/update-nxw.js +2 -6
  184. package/src/migrations/update-17-0-0/rm-default-collection-npm-scope.js +3 -3
  185. package/src/migrations/update-17-0-0/use-minimal-config-for-tasks-runner-options.js +49 -13
  186. package/src/migrations/update-17-2-0/move-default-base.d.ts +5 -0
  187. package/src/migrations/update-17-2-0/move-default-base.js +21 -0
  188. package/src/migrations/update-17-3-0/nx-release-path.d.ts +3 -0
  189. package/src/migrations/update-17-3-0/nx-release-path.js +48 -0
  190. package/src/migrations/update-17-3-0/update-nxw.d.ts +2 -0
  191. package/src/migrations/update-17-3-0/update-nxw.js +7 -0
  192. package/src/migrations/update-18-0-0/disable-crystal-for-existing-workspaces.d.ts +2 -0
  193. package/src/migrations/update-18-0-0/disable-crystal-for-existing-workspaces.js +9 -0
  194. package/src/native/index.d.ts +65 -15
  195. package/src/native/index.js +6 -2
  196. package/src/native/transform-objects.js +2 -0
  197. package/src/nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud.d.ts +1 -0
  198. package/src/nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud.js +13 -12
  199. package/src/nx-cloud/generators/connect-to-nx-cloud/schema.json +6 -1
  200. package/src/nx-cloud/update-manager.js +2 -1
  201. package/src/plugins/js/index.d.ts +1 -1
  202. package/src/plugins/js/index.js +3 -3
  203. package/src/plugins/js/lock-file/lock-file.d.ts +2 -2
  204. package/src/plugins/js/lock-file/lock-file.js +15 -3
  205. package/src/plugins/js/package-json/create-package-json.js +1 -1
  206. package/src/plugins/js/project-graph/build-dependencies/strip-source-code.d.ts +1 -1
  207. package/src/plugins/js/project-graph/build-dependencies/strip-source-code.js +1 -1
  208. package/src/plugins/js/project-graph/build-dependencies/typescript-import-locator.d.ts +1 -1
  209. package/src/plugins/js/project-graph/build-dependencies/typescript-import-locator.js +1 -1
  210. package/src/plugins/js/utils/register.d.ts +1 -1
  211. package/src/plugins/js/utils/register.js +28 -8
  212. package/src/plugins/js/versions.d.ts +1 -1
  213. package/src/plugins/js/versions.js +1 -1
  214. package/{plugins/package-json-workspaces.d.ts → src/plugins/package-json-workspaces/create-nodes.d.ts} +4 -4
  215. package/{plugins/package-json-workspaces.js → src/plugins/package-json-workspaces/create-nodes.js} +34 -12
  216. package/src/plugins/package-json-workspaces/index.d.ts +1 -0
  217. package/src/plugins/package-json-workspaces/index.js +4 -0
  218. package/src/plugins/project-json/build-nodes/package-json-next-to-project-json.d.ts +2 -0
  219. package/src/plugins/project-json/build-nodes/package-json-next-to-project-json.js +47 -0
  220. package/src/plugins/project-json/build-nodes/project-json.d.ts +2 -5
  221. package/src/plugins/project-json/build-nodes/project-json.js +6 -44
  222. package/src/plugins/target-defaults/target-defaults-plugin.d.ts +66 -0
  223. package/src/plugins/target-defaults/target-defaults-plugin.js +182 -0
  224. package/src/project-graph/affected/locators/project-glob-changes.js +3 -3
  225. package/src/project-graph/affected/locators/workspace-projects.d.ts +0 -2
  226. package/src/project-graph/affected/locators/workspace-projects.js +16 -29
  227. package/src/project-graph/build-project-graph.d.ts +20 -1
  228. package/src/project-graph/build-project-graph.js +91 -33
  229. package/src/project-graph/file-map-utils.d.ts +9 -5
  230. package/src/project-graph/file-map-utils.js +16 -63
  231. package/src/project-graph/file-utils.d.ts +1 -1
  232. package/src/project-graph/file-utils.js +44 -2
  233. package/src/project-graph/nx-deps-cache.js +1 -1
  234. package/src/project-graph/project-graph-builder.d.ts +1 -1
  235. package/src/project-graph/project-graph-builder.js +1 -1
  236. package/src/project-graph/project-graph.d.ts +32 -1
  237. package/src/project-graph/project-graph.js +147 -23
  238. package/src/project-graph/utils/build-all-workspace-files.d.ts +2 -0
  239. package/src/project-graph/utils/build-all-workspace-files.js +15 -0
  240. package/src/project-graph/utils/normalize-project-nodes.d.ts +2 -3
  241. package/src/project-graph/utils/normalize-project-nodes.js +7 -33
  242. package/src/project-graph/utils/project-configuration-utils.d.ts +66 -4
  243. package/src/project-graph/utils/project-configuration-utils.js +509 -62
  244. package/src/project-graph/utils/retrieve-workspace-files.d.ts +12 -31
  245. package/src/project-graph/utils/retrieve-workspace-files.js +27 -90
  246. package/src/tasks-runner/batch/run-batch.js +3 -3
  247. package/src/tasks-runner/cache.js +6 -3
  248. package/src/tasks-runner/create-task-graph.js +1 -1
  249. package/src/tasks-runner/fork.d.ts +1 -0
  250. package/src/tasks-runner/fork.js +23 -0
  251. package/src/tasks-runner/forked-process-task-runner.d.ts +13 -5
  252. package/src/tasks-runner/forked-process-task-runner.js +112 -21
  253. package/src/tasks-runner/init-tasks-runner.js +1 -1
  254. package/src/tasks-runner/life-cycles/dynamic-run-many-terminal-output-life-cycle.js +28 -28
  255. package/src/tasks-runner/life-cycles/dynamic-run-one-terminal-output-life-cycle.js +19 -22
  256. package/src/tasks-runner/life-cycles/empty-terminal-output-life-cycle.js +1 -3
  257. package/src/tasks-runner/life-cycles/invoke-runner-terminal-output-life-cycle.js +1 -3
  258. package/src/tasks-runner/life-cycles/static-run-many-terminal-output-life-cycle.js +2 -4
  259. package/src/tasks-runner/life-cycles/static-run-one-terminal-output-life-cycle.d.ts +1 -0
  260. package/src/tasks-runner/life-cycles/static-run-one-terminal-output-life-cycle.js +11 -5
  261. package/src/tasks-runner/life-cycles/view-logs-utils.js +1 -1
  262. package/src/tasks-runner/pseudo-ipc.d.ts +49 -0
  263. package/src/tasks-runner/pseudo-ipc.js +140 -0
  264. package/src/tasks-runner/pseudo-terminal.d.ts +43 -0
  265. package/src/tasks-runner/pseudo-terminal.js +159 -0
  266. package/src/tasks-runner/run-command.d.ts +1 -1
  267. package/src/tasks-runner/run-command.js +13 -19
  268. package/src/tasks-runner/task-env.js +1 -2
  269. package/src/tasks-runner/task-graph-utils.d.ts +7 -3
  270. package/src/tasks-runner/task-graph-utils.js +15 -15
  271. package/src/tasks-runner/task-orchestrator.js +68 -12
  272. package/src/tasks-runner/tasks-schedule.js +3 -3
  273. package/src/tasks-runner/utils.d.ts +16 -7
  274. package/src/tasks-runner/utils.js +30 -16
  275. package/src/utils/ab-testing.d.ts +36 -2
  276. package/src/utils/ab-testing.js +34 -16
  277. package/src/utils/assert-workspace-validity.js +1 -1
  278. package/src/utils/cache-directory.d.ts +1 -0
  279. package/src/utils/cache-directory.js +5 -1
  280. package/src/utils/child-process.d.ts +15 -1
  281. package/src/utils/child-process.js +91 -1
  282. package/src/utils/command-line-utils.js +2 -1
  283. package/src/utils/exit-codes.d.ts +6 -0
  284. package/src/utils/exit-codes.js +20 -0
  285. package/src/utils/fileutils.d.ts +1 -0
  286. package/src/utils/find-matching-projects.js +5 -5
  287. package/src/utils/find-workspace-root.js +1 -0
  288. package/src/utils/ignore.js +8 -1
  289. package/src/utils/json-diff.d.ts +1 -0
  290. package/src/utils/json-diff.js +2 -1
  291. package/src/utils/json.js +3 -1
  292. package/src/utils/logger.js +1 -1
  293. package/src/utils/nx-cloud-utils.d.ts +1 -1
  294. package/src/utils/nx-cloud-utils.js +9 -4
  295. package/src/utils/nx-plugin.d.ts +32 -15
  296. package/src/utils/nx-plugin.deprecated.d.ts +10 -5
  297. package/src/utils/nx-plugin.deprecated.js +23 -0
  298. package/src/utils/nx-plugin.js +71 -80
  299. package/src/utils/output.d.ts +4 -2
  300. package/src/utils/output.js +44 -9
  301. package/src/utils/package-json.d.ts +4 -4
  302. package/src/utils/package-json.js +18 -12
  303. package/src/utils/package-manager.d.ts +7 -0
  304. package/src/utils/package-manager.js +24 -4
  305. package/src/utils/params.d.ts +12 -4
  306. package/src/utils/params.js +37 -4
  307. package/src/utils/plugins/core-plugins.js +8 -0
  308. package/src/utils/plugins/installed-plugins.d.ts +2 -1
  309. package/src/utils/plugins/installed-plugins.js +2 -2
  310. package/src/utils/plugins/local-plugins.js +1 -1
  311. package/src/utils/plugins/plugin-capabilities.d.ts +3 -2
  312. package/src/utils/plugins/plugin-capabilities.js +7 -7
  313. package/src/utils/typescript.js +1 -1
  314. package/src/utils/update-nxw.d.ts +2 -0
  315. package/src/utils/update-nxw.js +12 -0
  316. package/src/utils/workspace-configuration-check.js +1 -1
  317. package/src/utils/workspace-context.d.ts +6 -4
  318. package/src/utils/workspace-context.js +19 -9
  319. package/src/core/graph/polyfills.js +0 -1
  320. /package/src/command-line/init/{init.d.ts → init-v1.d.ts} +0 -0
@@ -1,17 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.changelogHandler = void 0;
3
+ exports.shouldCreateGitHubRelease = exports.releaseChangelog = exports.releaseChangelogCLIHandler = void 0;
4
4
  const chalk = require("chalk");
5
+ const enquirer_1 = require("enquirer");
5
6
  const node_fs_1 = require("node:fs");
6
7
  const semver_1 = require("semver");
7
8
  const tmp_1 = require("tmp");
8
9
  const nx_json_1 = require("../../config/nx-json");
9
10
  const tree_1 = require("../../generators/tree");
10
11
  const register_1 = require("../../plugins/js/utils/register");
12
+ const file_map_utils_1 = require("../../project-graph/file-map-utils");
11
13
  const project_graph_1 = require("../../project-graph/project-graph");
12
14
  const utils_1 = require("../../tasks-runner/utils");
13
- const logger_1 = require("../../utils/logger");
15
+ const is_ci_1 = require("../../utils/is-ci");
14
16
  const output_1 = require("../../utils/output");
17
+ const params_1 = require("../../utils/params");
15
18
  const path_1 = require("../../utils/path");
16
19
  const typescript_1 = require("../../utils/typescript");
17
20
  const workspace_root_1 = require("../../utils/workspace-root");
@@ -22,100 +25,345 @@ const github_1 = require("./utils/github");
22
25
  const launch_editor_1 = require("./utils/launch-editor");
23
26
  const markdown_1 = require("./utils/markdown");
24
27
  const print_changes_1 = require("./utils/print-changes");
25
- class ReleaseVersion {
26
- constructor({ version, // short form version string with no prefixes or patterns, e.g. 1.0.0
27
- releaseTagPattern, // full pattern to interpolate, e.g. "v{version}" or "{projectName}@{version}"
28
- projectName, // optional project name to interpolate into the releaseTagPattern
29
- }) {
30
- this.rawVersion = version;
31
- this.gitTag = (0, utils_1.interpolate)(releaseTagPattern, {
32
- version,
33
- projectName,
34
- });
35
- this.isPrerelease = isPrerelease(version);
36
- }
37
- }
38
- async function changelogHandler(args) {
39
- // Right now, the given version must be valid semver in order to proceed
40
- if (!(0, semver_1.valid)(args.version)) {
41
- output_1.output.error({
42
- title: `The given version "${args.version}" is not a valid semver version. Please provide your version in the format "1.0.0", "1.0.0-beta.1" etc`,
43
- });
44
- process.exit(1);
45
- }
28
+ const resolve_nx_json_error_message_1 = require("./utils/resolve-nx-json-error-message");
29
+ const shared_1 = require("./utils/shared");
30
+ const releaseChangelogCLIHandler = (args) => (0, params_1.handleErrors)(args.verbose, () => releaseChangelog(args));
31
+ exports.releaseChangelogCLIHandler = releaseChangelogCLIHandler;
32
+ /**
33
+ * NOTE: This function is also exported for programmatic usage and forms part of the public API
34
+ * of Nx. We intentionally do not wrap the implementation with handleErrors because users need
35
+ * to have control over their own error handling when using the API.
36
+ */
37
+ async function releaseChangelog(args) {
46
38
  const projectGraph = await (0, project_graph_1.createProjectGraphAsync)({ exitOnError: true });
47
39
  const nxJson = (0, nx_json_1.readNxJson)();
48
40
  if (args.verbose) {
49
41
  process.env.NX_VERBOSE_LOGGING = 'true';
50
42
  }
51
43
  // Apply default configuration to any optional user configuration
52
- const { error: configError, nxReleaseConfig } = await (0, config_1.createNxReleaseConfig)(projectGraph, nxJson.release);
44
+ const { error: configError, nxReleaseConfig } = await (0, config_1.createNxReleaseConfig)(projectGraph, await (0, file_map_utils_1.createProjectFileMapUsingProjectGraph)(projectGraph), nxJson.release);
53
45
  if (configError) {
54
46
  return await (0, config_1.handleNxReleaseConfigError)(configError);
55
47
  }
48
+ // The nx release top level command will always override these three git args. This is how we can tell
49
+ // if the top level release command was used or if the user is using the changelog subcommand.
50
+ // If the user explicitly overrides these args, then it doesn't matter if the top level config is set,
51
+ // as all of the git options would be overridden anyway.
52
+ if ((args.gitCommit === undefined ||
53
+ args.gitTag === undefined ||
54
+ args.stageChanges === undefined) &&
55
+ nxJson.release?.git) {
56
+ const nxJsonMessage = await (0, resolve_nx_json_error_message_1.resolveNxJsonConfigErrorMessage)([
57
+ 'release',
58
+ 'git',
59
+ ]);
60
+ output_1.output.error({
61
+ title: `The "release.git" property in nx.json may not be used with the "nx release changelog" subcommand or programmatic API. Instead, configure git options for subcommands directly with "release.version.git" and "release.changelog.git".`,
62
+ bodyLines: [nxJsonMessage],
63
+ });
64
+ process.exit(1);
65
+ }
56
66
  const { error: filterError, releaseGroups, releaseGroupToFilteredProjects, } = (0, filter_release_groups_1.filterReleaseGroups)(projectGraph, nxReleaseConfig, args.projects, args.groups);
57
67
  if (filterError) {
58
68
  output_1.output.error(filterError);
59
69
  process.exit(1);
60
70
  }
61
- const releaseVersion = new ReleaseVersion({
62
- version: args.version,
63
- releaseTagPattern: nxReleaseConfig.releaseTagPattern,
64
- });
65
- const from = args.from || (await (0, git_1.getLastGitTag)());
66
- if (!from) {
67
- output_1.output.error({
68
- title: `Unable to determine the previous git tag, please provide an explicit git reference using --from`,
71
+ const changelogGenerationEnabled = !!nxReleaseConfig.changelog.workspaceChangelog ||
72
+ Object.values(nxReleaseConfig.groups).some((g) => g.changelog);
73
+ if (!changelogGenerationEnabled) {
74
+ output_1.output.warn({
75
+ title: `Changelogs are disabled. No changelog entries will be generated`,
76
+ bodyLines: [
77
+ `To explicitly enable changelog generation, configure "release.changelog.workspaceChangelog" or "release.changelog.projectChangelogs" in nx.json.`,
78
+ ],
69
79
  });
70
- process.exit(1);
80
+ return {};
81
+ }
82
+ const useAutomaticFromRef = nxReleaseConfig.changelog?.automaticFromRef || args.firstRelease;
83
+ /**
84
+ * For determining the versions to use within changelog files, there are a few different possibilities:
85
+ * - the user is using the nx CLI, and therefore passes a single --version argument which represents the version for any and all changelog
86
+ * files which will be generated (i.e. both the workspace changelog, and all project changelogs, depending on which of those has been enabled)
87
+ * - the user is using the nxReleaseChangelog API programmatically, and:
88
+ * - passes only a version property
89
+ * - this works in the same way as described above for the CLI
90
+ * - passes only a versionData object
91
+ * - this is a special case where the user is providing a version for each project, and therefore the version argument is not needed
92
+ * - NOTE: it is not possible to generate a workspace level changelog with only a versionData object, and this will produce an error
93
+ * - passes both a version and a versionData object
94
+ * - in this case, the version property will be used as the reference for the workspace changelog, and the versionData object will be used
95
+ * to generate project changelogs
96
+ */
97
+ const { workspaceChangelogVersion, projectsVersionData } = resolveChangelogVersions(args, releaseGroups, releaseGroupToFilteredProjects);
98
+ const to = args.to || 'HEAD';
99
+ const toSHA = await (0, git_1.getCommitHash)(to);
100
+ const headSHA = to === 'HEAD' ? toSHA : await (0, git_1.getCommitHash)('HEAD');
101
+ /**
102
+ * Protect the user against attempting to create a new commit when recreating an old release changelog,
103
+ * this seems like it would always be unintentional.
104
+ */
105
+ const autoCommitEnabled = args.gitCommit ?? nxReleaseConfig.changelog.git.commit;
106
+ if (autoCommitEnabled && headSHA !== toSHA) {
107
+ throw new Error(`You are attempting to recreate the changelog for an old release, but you have enabled auto-commit mode. Please disable auto-commit mode by updating your nx.json, or passing --git-commit=false`);
71
108
  }
72
- const rawCommits = await (0, git_1.getGitDiff)(from, args.to);
73
- // Parse as conventional commits
74
- const commits = (0, git_1.parseCommits)(rawCommits).filter((c) => {
75
- const type = c.type;
76
- // Always ignore non user-facing commits for now
77
- // TODO: allow this filter to be configurable via config in a future release
78
- if (type === 'feat' || type === 'fix' || type === 'perf') {
79
- return true;
80
- }
81
- return false;
82
- });
83
109
  const tree = new tree_1.FsTree(workspace_root_1.workspaceRoot, args.verbose);
84
- await generateChangelogForWorkspace(tree, releaseVersion, !!args.dryRun,
85
- // Only trigger interactive mode for the workspace changelog if the user explicitly requested it via "all" or "workspace"
86
- args.interactive === 'all' || args.interactive === 'workspace', commits, nxReleaseConfig.changelog.workspaceChangelog, args.gitRemote);
87
- if (args.projects?.length) {
88
- /**
89
- * Run changelog generation for all remaining release groups and filtered projects within them
90
- */
91
- for (const releaseGroup of releaseGroups) {
92
- const projectNodes = Array.from(releaseGroupToFilteredProjects.get(releaseGroup)).map((name) => projectGraph.nodes[name]);
93
- await generateChangelogForProjects(tree, args.version, !!args.dryRun,
94
- // Only trigger interactive mode for the workspace changelog if the user explicitly requested it via "all" or "projects"
95
- args.interactive === 'all' || args.interactive === 'projects', commits, releaseGroup.changelog, releaseGroup.releaseTagPattern, projectNodes, args.gitRemote);
110
+ const commitMessage = args.gitCommitMessage || nxReleaseConfig.changelog.git.commitMessage;
111
+ const commitMessageValues = (0, shared_1.createCommitMessageValues)(releaseGroups, releaseGroupToFilteredProjects, projectsVersionData, commitMessage);
112
+ // Resolve any git tags as early as possible so that we can hard error in case of any duplicates before reaching the actual git command
113
+ const gitTagValues = args.gitTag ?? nxReleaseConfig.changelog.git.tag
114
+ ? (0, shared_1.createGitTagValues)(releaseGroups, releaseGroupToFilteredProjects, projectsVersionData)
115
+ : [];
116
+ (0, shared_1.handleDuplicateGitTags)(gitTagValues);
117
+ const postGitTasks = [];
118
+ let workspaceChangelogFromRef = args.from ||
119
+ (await (0, git_1.getLatestGitTagForPattern)(nxReleaseConfig.releaseTagPattern))?.tag;
120
+ if (!workspaceChangelogFromRef) {
121
+ if (useAutomaticFromRef) {
122
+ workspaceChangelogFromRef = await (0, git_1.getFirstGitCommit)();
123
+ if (args.verbose) {
124
+ console.log(`Determined workspace --from ref from the first commit in workspace: ${workspaceChangelogFromRef}`);
125
+ }
126
+ }
127
+ else {
128
+ throw new Error(`Unable to determine the previous git tag. If this is the first release of your workspace, use the --first-release option or set the "release.changelog.automaticFromRef" config property in nx.json to generate a changelog from the first commit. Otherwise, be sure to configure the "release.releaseTagPattern" property in nx.json to match the structure of your repository's git tags.`);
96
129
  }
97
- return process.exit(0);
98
130
  }
99
- /**
100
- * Run changelog generation for all remaining release groups
101
- */
131
+ // Make sure that the fromRef is actually resolvable
132
+ const workspaceChangelogFromSHA = await (0, git_1.getCommitHash)(workspaceChangelogFromRef);
133
+ const workspaceChangelogCommits = await getCommits(workspaceChangelogFromSHA, toSHA, nxReleaseConfig.conventionalCommits);
134
+ const workspaceChangelog = await generateChangelogForWorkspace(tree, args, projectGraph, nxReleaseConfig, workspaceChangelogVersion, workspaceChangelogCommits);
135
+ if (workspaceChangelog &&
136
+ shouldCreateGitHubRelease(nxReleaseConfig.changelog.workspaceChangelog, args.createRelease)) {
137
+ let hasPushed = false;
138
+ postGitTasks.push(async (latestCommit) => {
139
+ if (!hasPushed) {
140
+ output_1.output.logSingleLine(`Pushing to git remote`);
141
+ // Before we can create/update the release we need to ensure the commit exists on the remote
142
+ await (0, git_1.gitPush)({
143
+ gitRemote: args.gitRemote,
144
+ dryRun: args.dryRun,
145
+ verbose: args.verbose,
146
+ });
147
+ hasPushed = true;
148
+ }
149
+ output_1.output.logSingleLine(`Creating GitHub Release`);
150
+ await (0, github_1.createOrUpdateGithubRelease)(workspaceChangelog.releaseVersion, workspaceChangelog.contents, latestCommit, { dryRun: args.dryRun });
151
+ });
152
+ }
153
+ const allProjectChangelogs = {};
102
154
  for (const releaseGroup of releaseGroups) {
103
- const projectNodes = releaseGroup.projects.map((name) => projectGraph.nodes[name]);
104
- await generateChangelogForProjects(tree, args.version, !!args.dryRun,
105
- // Only trigger interactive mode for the workspace changelog if the user explicitly requested it via "all" or "projects"
106
- args.interactive === 'all' || args.interactive === 'projects', commits, releaseGroup.changelog, releaseGroup.releaseTagPattern, projectNodes, args.gitRemote);
155
+ const config = releaseGroup.changelog;
156
+ // The entire feature is disabled at the release group level, exit early
157
+ if (config === false) {
158
+ continue;
159
+ }
160
+ const projects = args.projects?.length
161
+ ? // If the user has passed a list of projects, we need to use the filtered list of projects within the release group
162
+ Array.from(releaseGroupToFilteredProjects.get(releaseGroup))
163
+ : // Otherwise, we use the full list of projects within the release group
164
+ releaseGroup.projects;
165
+ const projectNodes = projects.map((name) => projectGraph.nodes[name]);
166
+ if (releaseGroup.projectsRelationship === 'independent') {
167
+ for (const project of projectNodes) {
168
+ let fromRef = args.from ||
169
+ (await (0, git_1.getLatestGitTagForPattern)(releaseGroup.releaseTagPattern, {
170
+ projectName: project.name,
171
+ }))?.tag;
172
+ let commits = null;
173
+ if (!fromRef && useAutomaticFromRef) {
174
+ const firstCommit = await (0, git_1.getFirstGitCommit)();
175
+ const allCommits = await getCommits(firstCommit, toSHA, nxReleaseConfig.conventionalCommits);
176
+ const commitsForProject = allCommits.filter((c) => c.affectedFiles.find((f) => f.startsWith(project.data.root)));
177
+ fromRef = commitsForProject[0]?.shortHash;
178
+ if (args.verbose) {
179
+ console.log(`Determined --from ref for ${project.name} from the first commit in which it exists: ${fromRef}`);
180
+ }
181
+ commits = commitsForProject;
182
+ }
183
+ if (!fromRef && !commits) {
184
+ throw new Error(`Unable to determine the previous git tag. If this is the first release of your workspace, use the --first-release option or set the "release.changelog.automaticFromRef" config property in nx.json to generate a changelog from the first commit. Otherwise, be sure to configure the "release.releaseTagPattern" property in nx.json to match the structure of your repository's git tags.`);
185
+ }
186
+ if (!commits) {
187
+ commits = await getCommits(fromRef, toSHA, nxReleaseConfig.conventionalCommits);
188
+ }
189
+ const projectChangelogs = await generateChangelogForProjects(tree, args, projectGraph, commits, projectsVersionData, releaseGroup, [project], nxReleaseConfig);
190
+ let hasPushed = false;
191
+ for (const [projectName, projectChangelog] of Object.entries(projectChangelogs)) {
192
+ if (projectChangelogs &&
193
+ shouldCreateGitHubRelease(releaseGroup.changelog, args.createRelease)) {
194
+ postGitTasks.push(async (latestCommit) => {
195
+ if (!hasPushed) {
196
+ output_1.output.logSingleLine(`Pushing to git remote`);
197
+ // Before we can create/update the release we need to ensure the commit exists on the remote
198
+ await (0, git_1.gitPush)({
199
+ gitRemote: args.gitRemote,
200
+ dryRun: args.dryRun,
201
+ verbose: args.verbose,
202
+ });
203
+ hasPushed = true;
204
+ }
205
+ output_1.output.logSingleLine(`Creating GitHub Release`);
206
+ await (0, github_1.createOrUpdateGithubRelease)(projectChangelog.releaseVersion, projectChangelog.contents, latestCommit, { dryRun: args.dryRun });
207
+ });
208
+ }
209
+ allProjectChangelogs[projectName] = projectChangelog;
210
+ }
211
+ }
212
+ }
213
+ else {
214
+ const fromRef = args.from ||
215
+ (await (0, git_1.getLatestGitTagForPattern)(releaseGroup.releaseTagPattern))?.tag;
216
+ if (!fromRef) {
217
+ throw new Error(`Unable to determine the previous git tag, please provide an explicit git reference using --from`);
218
+ }
219
+ // Make sure that the fromRef is actually resolvable
220
+ const fromSHA = await (0, git_1.getCommitHash)(fromRef);
221
+ const commits = await getCommits(fromSHA, toSHA, nxReleaseConfig.conventionalCommits);
222
+ const projectChangelogs = await generateChangelogForProjects(tree, args, projectGraph, commits, projectsVersionData, releaseGroup, projectNodes, nxReleaseConfig);
223
+ let hasPushed = false;
224
+ for (const [projectName, projectChangelog] of Object.entries(projectChangelogs)) {
225
+ if (projectChangelogs &&
226
+ shouldCreateGitHubRelease(releaseGroup.changelog, args.createRelease)) {
227
+ postGitTasks.push(async (latestCommit) => {
228
+ if (!hasPushed) {
229
+ output_1.output.logSingleLine(`Pushing to git remote`);
230
+ // Before we can create/update the release we need to ensure the commit exists on the remote
231
+ await (0, git_1.gitPush)({
232
+ gitRemote: args.gitRemote,
233
+ dryRun: args.dryRun,
234
+ verbose: args.verbose,
235
+ });
236
+ hasPushed = true;
237
+ }
238
+ output_1.output.logSingleLine(`Creating GitHub Release`);
239
+ await (0, github_1.createOrUpdateGithubRelease)(projectChangelog.releaseVersion, projectChangelog.contents, latestCommit, { dryRun: args.dryRun });
240
+ });
241
+ }
242
+ allProjectChangelogs[projectName] = projectChangelog;
243
+ }
244
+ }
107
245
  }
108
- if (args.dryRun) {
109
- logger_1.logger.warn(`\nNOTE: The "dryRun" flag means no changelogs were actually created.`);
246
+ await applyChangesAndExit(args, nxReleaseConfig, tree, toSHA, postGitTasks, commitMessageValues, gitTagValues);
247
+ return {
248
+ workspaceChangelog,
249
+ projectChangelogs: allProjectChangelogs,
250
+ };
251
+ }
252
+ exports.releaseChangelog = releaseChangelog;
253
+ function resolveChangelogVersions(args, releaseGroups, releaseGroupToFilteredProjects) {
254
+ if (!args.version && !args.versionData) {
255
+ throw new Error(`You must provide a version string and/or a versionData object.`);
110
256
  }
111
- process.exit(0);
257
+ /**
258
+ * TODO: revaluate this assumption holistically in a dedicated PR when we add support for calver
259
+ * (e.g. the Release class also uses semver utils to check if prerelease).
260
+ *
261
+ * Right now, the given version must be valid semver in order to proceed
262
+ */
263
+ if (args.version && !(0, semver_1.valid)(args.version)) {
264
+ throw new Error(`The given version "${args.version}" is not a valid semver version. Please provide your version in the format "1.0.0", "1.0.0-beta.1" etc`);
265
+ }
266
+ const versionData = releaseGroups.reduce((versionData, releaseGroup) => {
267
+ const releaseGroupProjectNames = Array.from(releaseGroupToFilteredProjects.get(releaseGroup));
268
+ for (const projectName of releaseGroupProjectNames) {
269
+ if (!args.versionData) {
270
+ versionData[projectName] = {
271
+ newVersion: args.version,
272
+ currentVersion: '', // not relevant within changelog/commit generation
273
+ dependentProjects: [], // not relevant within changelog/commit generation
274
+ };
275
+ continue;
276
+ }
277
+ /**
278
+ * In the case where a versionData object was provided, we need to make sure all projects are present,
279
+ * otherwise it suggests a filtering mismatch between the version and changelog command invocations.
280
+ */
281
+ if (!args.versionData[projectName]) {
282
+ throw new Error(`The provided versionData object does not contain a version for project "${projectName}". This suggests a filtering mismatch between the version and changelog command invocations.`);
283
+ }
284
+ }
285
+ return versionData;
286
+ }, args.versionData || {});
287
+ return {
288
+ workspaceChangelogVersion: args.version,
289
+ projectsVersionData: versionData,
290
+ };
112
291
  }
113
- exports.changelogHandler = changelogHandler;
114
- function isPrerelease(version) {
115
- // prerelease returns an array of matching prerelease "components", or null if the version is not a prerelease
116
- return (0, semver_1.prerelease)(version) !== null;
292
+ async function applyChangesAndExit(args, nxReleaseConfig, tree, toSHA, postGitTasks, commitMessageValues, gitTagValues) {
293
+ let latestCommit = toSHA;
294
+ const changes = tree.listChanges();
295
+ /**
296
+ * In the case where we are expecting changelog file updates, but there is nothing
297
+ * to flush from the tree, we exit early. This could happen we using conventional
298
+ * commits, for example.
299
+ */
300
+ const changelogFilesEnabled = checkChangelogFilesEnabled(nxReleaseConfig);
301
+ if (changelogFilesEnabled && !changes.length) {
302
+ output_1.output.warn({
303
+ title: `No changes detected for changelogs`,
304
+ bodyLines: [
305
+ `No changes were detected for any changelog files, so no changelog entries will be generated.`,
306
+ ],
307
+ });
308
+ if (!postGitTasks.length) {
309
+ // no GitHub releases to create so we can just exit
310
+ return;
311
+ }
312
+ if ((0, is_ci_1.isCI)()) {
313
+ output_1.output.warn({
314
+ title: `Skipped GitHub release creation because no changes were detected for any changelog files.`,
315
+ });
316
+ return;
317
+ }
318
+ // prompt the user to see if they want to create a GitHub release anyway
319
+ // we know that the user has configured GitHub releases because we have postGitTasks
320
+ const shouldCreateGitHubReleaseAnyway = await promptForGitHubRelease();
321
+ if (!shouldCreateGitHubReleaseAnyway) {
322
+ return;
323
+ }
324
+ for (const postGitTask of postGitTasks) {
325
+ await postGitTask(latestCommit);
326
+ }
327
+ return;
328
+ }
329
+ // Generate a new commit for the changes, if configured to do so
330
+ if (args.gitCommit ?? nxReleaseConfig.changelog.git.commit) {
331
+ await (0, shared_1.commitChanges)(changes.map((f) => f.path), !!args.dryRun, !!args.verbose, commitMessageValues, args.gitCommitArgs || nxReleaseConfig.changelog.git.commitArgs);
332
+ // Resolve the commit we just made
333
+ latestCommit = await (0, git_1.getCommitHash)('HEAD');
334
+ }
335
+ else if ((args.stageChanges ?? nxReleaseConfig.changelog.git.stageChanges) &&
336
+ changes.length) {
337
+ output_1.output.logSingleLine(`Staging changed files with git`);
338
+ await (0, git_1.gitAdd)({
339
+ changedFiles: changes.map((f) => f.path),
340
+ dryRun: args.dryRun,
341
+ verbose: args.verbose,
342
+ });
343
+ }
344
+ // Generate a one or more git tags for the changes, if configured to do so
345
+ if (args.gitTag ?? nxReleaseConfig.changelog.git.tag) {
346
+ output_1.output.logSingleLine(`Tagging commit with git`);
347
+ for (const tag of gitTagValues) {
348
+ await (0, git_1.gitTag)({
349
+ tag,
350
+ message: args.gitTagMessage || nxReleaseConfig.changelog.git.tagMessage,
351
+ additionalArgs: args.gitTagArgs || nxReleaseConfig.changelog.git.tagArgs,
352
+ dryRun: args.dryRun,
353
+ verbose: args.verbose,
354
+ });
355
+ }
356
+ }
357
+ // Run any post-git tasks in series
358
+ for (const postGitTask of postGitTasks) {
359
+ await postGitTask(latestCommit);
360
+ }
361
+ return;
117
362
  }
118
363
  function resolveChangelogRenderer(changelogRendererPath) {
364
+ const interpolatedChangelogRendererPath = (0, utils_1.interpolate)(changelogRendererPath, {
365
+ workspaceRoot: workspace_root_1.workspaceRoot,
366
+ });
119
367
  // Try and load the provided (or default) changelog renderer
120
368
  let changelogRenderer;
121
369
  let cleanupTranspiler = () => { };
@@ -124,7 +372,7 @@ function resolveChangelogRenderer(changelogRendererPath) {
124
372
  if (rootTsconfigPath) {
125
373
  cleanupTranspiler = (0, register_1.registerTsProject)(rootTsconfigPath);
126
374
  }
127
- const r = require(changelogRendererPath);
375
+ const r = require(interpolatedChangelogRendererPath);
128
376
  changelogRenderer = r.default || r;
129
377
  }
130
378
  catch {
@@ -134,45 +382,74 @@ function resolveChangelogRenderer(changelogRendererPath) {
134
382
  }
135
383
  return changelogRenderer;
136
384
  }
137
- async function generateChangelogForWorkspace(tree, releaseVersion, dryRun, interactive, commits, config, gitRemote) {
385
+ async function generateChangelogForWorkspace(tree, args, projectGraph, nxReleaseConfig, workspaceChangelogVersion, commits) {
386
+ const config = nxReleaseConfig.changelog.workspaceChangelog;
138
387
  // The entire feature is disabled at the workspace level, exit early
139
388
  if (config === false) {
140
389
  return;
141
390
  }
391
+ // If explicitly null it must mean that no changes were detected (e.g. when using conventional commits), so do nothing
392
+ if (workspaceChangelogVersion === null) {
393
+ return;
394
+ }
395
+ // The user explicitly passed workspaceChangelog=true but does not have a workspace changelog config in nx.json
396
+ if (!config) {
397
+ throw new Error(`Workspace changelog is enabled but no configuration was provided. Please provide a workspaceChangelog object in your nx.json`);
398
+ }
399
+ if (Object.entries(nxReleaseConfig.groups).length > 1) {
400
+ output_1.output.warn({
401
+ title: `Workspace changelog is enabled, but you have multiple release groups configured. This is not supported, so workspace changelog will be disabled.`,
402
+ bodyLines: [
403
+ `A single workspace version cannot be determined when defining multiple release groups because versions differ between each group.`,
404
+ `Project level changelogs can be enabled with the "release.changelog.projectChangelogs" property.`,
405
+ ],
406
+ });
407
+ return;
408
+ }
409
+ if (Object.values(nxReleaseConfig.groups)[0].projectsRelationship ===
410
+ 'independent') {
411
+ output_1.output.warn({
412
+ title: `Workspace changelog is enabled, but you have configured an independent projects relationship. This is not supported, so workspace changelog will be disabled.`,
413
+ bodyLines: [
414
+ `A single workspace version cannot be determined when using independent projects because versions differ between each project.`,
415
+ `Project level changelogs can be enabled with the "release.changelog.projectChangelogs" property.`,
416
+ ],
417
+ });
418
+ return;
419
+ }
420
+ // Only trigger interactive mode for the workspace changelog if the user explicitly requested it via "all" or "workspace"
421
+ const interactive = args.interactive === 'all' || args.interactive === 'workspace';
422
+ const dryRun = !!args.dryRun;
423
+ const gitRemote = args.gitRemote;
142
424
  const changelogRenderer = resolveChangelogRenderer(config.renderer);
143
425
  let interpolatedTreePath = config.file || '';
144
426
  if (interpolatedTreePath) {
145
427
  interpolatedTreePath = (0, utils_1.interpolate)(interpolatedTreePath, {
146
- projectName: '',
147
- projectRoot: '',
428
+ projectName: '', // n/a for the workspace changelog
429
+ projectRoot: '', // n/a for the workspace changelog
148
430
  workspaceRoot: '', // within the tree, workspaceRoot is the root
149
431
  });
150
432
  }
151
- // We are either creating/previewing a changelog file, a Github release, or both
152
- let logTitle = dryRun ? 'Previewing a' : 'Generating a';
153
- switch (true) {
154
- case interpolatedTreePath && config.createRelease === 'github':
155
- logTitle += ` Github release and an entry in ${interpolatedTreePath} for ${chalk.white(releaseVersion.gitTag)}`;
156
- break;
157
- case !!interpolatedTreePath:
158
- logTitle += `n entry in ${interpolatedTreePath} for ${chalk.white(releaseVersion.gitTag)}`;
159
- break;
160
- case config.createRelease === 'github':
161
- logTitle += ` Github release for ${chalk.white(releaseVersion.gitTag)}`;
162
- }
163
- output_1.output.log({
164
- title: logTitle,
433
+ const releaseVersion = new shared_1.ReleaseVersion({
434
+ version: workspaceChangelogVersion,
435
+ releaseTagPattern: nxReleaseConfig.releaseTagPattern,
165
436
  });
166
- const githubRepoSlug = config.createRelease === 'github'
167
- ? (0, github_1.getGitHubRepoSlug)(gitRemote)
168
- : undefined;
437
+ if (interpolatedTreePath) {
438
+ const prefix = dryRun ? 'Previewing' : 'Generating';
439
+ output_1.output.log({
440
+ title: `${prefix} an entry in ${interpolatedTreePath} for ${chalk.white(releaseVersion.gitTag)}`,
441
+ });
442
+ }
443
+ const githubRepoSlug = (0, github_1.getGitHubRepoSlug)(gitRemote);
169
444
  let contents = await changelogRenderer({
445
+ projectGraph,
170
446
  commits,
171
447
  releaseVersion: releaseVersion.rawVersion,
172
448
  project: null,
173
449
  repoSlug: githubRepoSlug,
174
450
  entryWhenNoChanges: config.entryWhenNoChanges,
175
451
  changelogRenderOptions: config.renderOptions,
452
+ conventionalCommitsConfig: nxReleaseConfig.conventionalCommits,
176
453
  });
177
454
  /**
178
455
  * If interactive mode, make the changelog contents available for the user to modify in their editor of choice,
@@ -187,14 +464,10 @@ async function generateChangelogForWorkspace(tree, releaseVersion, dryRun, inter
187
464
  await (0, launch_editor_1.launchEditor)(changelogPath);
188
465
  contents = (0, node_fs_1.readFileSync)(changelogPath, 'utf-8');
189
466
  }
190
- /**
191
- * The exact logic we use for printing the summary/diff to the user is dependent upon whether they are creating
192
- * a changelog file, a Github release, or both.
193
- */
194
- let printSummary = () => { };
195
- const noDiffInChangelogMessage = chalk.yellow(`NOTE: There was no diff detected for the changelog entry. Maybe you intended to pass alternative git references via --from and --to?`);
196
467
  if (interpolatedTreePath) {
197
- let rootChangelogContents = tree.read(interpolatedTreePath)?.toString() ?? '';
468
+ let rootChangelogContents = tree.exists(interpolatedTreePath)
469
+ ? tree.read(interpolatedTreePath).toString()
470
+ : '';
198
471
  if (rootChangelogContents) {
199
472
  // NOTE: right now existing releases are always expected to be in markdown format, but in the future we could potentially support others via a custom parser option
200
473
  const changelogReleases = (0, markdown_1.parseChangelogMarkdown)(rootChangelogContents).releases;
@@ -212,80 +485,25 @@ async function generateChangelogForWorkspace(tree, releaseVersion, dryRun, inter
212
485
  rootChangelogContents = contents;
213
486
  }
214
487
  tree.write(interpolatedTreePath, rootChangelogContents);
215
- printSummary = () => (0, print_changes_1.printChanges)(tree, !!dryRun, 3, false, noDiffInChangelogMessage);
216
- }
217
- if (config.createRelease === 'github') {
218
- if (!githubRepoSlug) {
219
- output_1.output.error({
220
- title: `Unable to create a Github release because the Github repo slug could not be determined.`,
221
- bodyLines: [
222
- `Please ensure you have a valid Github remote configured. You can run \`git remote -v\` to list your current remotes.`,
223
- ],
224
- });
225
- process.exit(1);
226
- }
227
- const token = await (0, github_1.resolveGithubToken)();
228
- const githubRequestConfig = {
229
- repo: githubRepoSlug,
230
- token,
231
- };
232
- let existingGithubReleaseForVersion;
233
- try {
234
- existingGithubReleaseForVersion = await (0, github_1.getGithubReleaseByTag)(githubRequestConfig, releaseVersion.gitTag);
235
- }
236
- catch (err) {
237
- if (err.response?.status === 401) {
238
- output_1.output.error({
239
- title: `Unable to resolve data via the Github API. You can use any of the following options to resolve this:`,
240
- bodyLines: [
241
- '- Set the `GITHUB_TOKEN` or `GH_TOKEN` environment variable to a valid Github token with `repo` scope',
242
- '- Have an active session via the official gh CLI tool (https://cli.github.com) in your current terminal',
243
- ],
244
- });
245
- process.exit(1);
246
- }
247
- if (err.response?.status === 404) {
248
- // No existing release found, this is fine
249
- }
250
- else {
251
- // Rethrow unknown errors for now
252
- throw err;
253
- }
254
- }
255
- let existingPrintSummaryFn = printSummary;
256
- printSummary = () => {
257
- const logTitle = `https://github.com/${githubRepoSlug}/releases/tag/${releaseVersion.gitTag}`;
258
- if (existingGithubReleaseForVersion) {
259
- console.error(`${chalk.white('UPDATE')} ${logTitle}${dryRun ? chalk.keyword('orange')(' [dry-run]') : ''}`);
260
- }
261
- else {
262
- console.error(`${chalk.green('CREATE')} ${logTitle}${dryRun ? chalk.keyword('orange')(' [dry-run]') : ''}`);
263
- }
264
- // Only print the diff here if we are not already going to be printing changes from the Tree
265
- if (!interpolatedTreePath) {
266
- console.log('');
267
- (0, print_changes_1.printDiff)(existingGithubReleaseForVersion
268
- ? existingGithubReleaseForVersion.body
269
- : '', contents, 3, noDiffInChangelogMessage);
270
- }
271
- existingPrintSummaryFn();
272
- };
273
- if (!dryRun) {
274
- await (0, github_1.createOrUpdateGithubRelease)(githubRequestConfig, {
275
- version: releaseVersion.gitTag,
276
- prerelease: releaseVersion.isPrerelease,
277
- body: contents,
278
- }, existingGithubReleaseForVersion);
279
- }
488
+ (0, print_changes_1.printAndFlushChanges)(tree, !!dryRun, 3, false, shared_1.noDiffInChangelogMessage);
280
489
  }
281
- printSummary();
490
+ return {
491
+ releaseVersion,
492
+ contents,
493
+ };
282
494
  }
283
- async function generateChangelogForProjects(tree, rawVersion, dryRun, interactive, commits, config, releaseTagPattern, projects, gitRemote) {
284
- // The entire feature is disabled at the project level, exit early
495
+ async function generateChangelogForProjects(tree, args, projectGraph, commits, projectsVersionData, releaseGroup, projects, nxReleaseConfig) {
496
+ const config = releaseGroup.changelog;
497
+ // The entire feature is disabled at the release group level, exit early
285
498
  if (config === false) {
286
499
  return;
287
500
  }
501
+ // Only trigger interactive mode for the project changelog if the user explicitly requested it via "all" or "projects"
502
+ const interactive = args.interactive === 'all' || args.interactive === 'projects';
503
+ const dryRun = !!args.dryRun;
504
+ const gitRemote = args.gitRemote;
288
505
  const changelogRenderer = resolveChangelogRenderer(config.renderer);
506
+ const projectChangelogs = {};
289
507
  for (const project of projects) {
290
508
  let interpolatedTreePath = config.file || '';
291
509
  if (interpolatedTreePath) {
@@ -295,33 +513,32 @@ async function generateChangelogForProjects(tree, rawVersion, dryRun, interactiv
295
513
  workspaceRoot: '', // within the tree, workspaceRoot is the root
296
514
  });
297
515
  }
298
- const releaseVersion = new ReleaseVersion({
299
- version: rawVersion,
300
- releaseTagPattern,
516
+ /**
517
+ * newVersion will be null in the case that no changes were detected (e.g. in conventional commits mode),
518
+ * no changelog entry is relevant in that case.
519
+ */
520
+ if (projectsVersionData[project.name].newVersion === null) {
521
+ continue;
522
+ }
523
+ const releaseVersion = new shared_1.ReleaseVersion({
524
+ version: projectsVersionData[project.name].newVersion,
525
+ releaseTagPattern: releaseGroup.releaseTagPattern,
301
526
  projectName: project.name,
302
527
  });
303
- // We are either creating/previewing a changelog file, a Github release, or both
304
- let logTitle = dryRun ? 'Previewing a' : 'Generating a';
305
- switch (true) {
306
- case interpolatedTreePath && config.createRelease === 'github':
307
- logTitle += ` Github release and an entry in ${interpolatedTreePath} for ${chalk.white(releaseVersion.gitTag)}`;
308
- break;
309
- case !!interpolatedTreePath:
310
- logTitle += `n entry in ${interpolatedTreePath} for ${chalk.white(releaseVersion.gitTag)}`;
311
- break;
312
- case config.createRelease === 'github':
313
- logTitle += ` Github release for ${chalk.white(releaseVersion.gitTag)}`;
528
+ if (interpolatedTreePath) {
529
+ const prefix = dryRun ? 'Previewing' : 'Generating';
530
+ output_1.output.log({
531
+ title: `${prefix} an entry in ${interpolatedTreePath} for ${chalk.white(releaseVersion.gitTag)}`,
532
+ });
314
533
  }
315
- output_1.output.log({
316
- title: logTitle,
317
- });
318
534
  const githubRepoSlug = config.createRelease === 'github'
319
535
  ? (0, github_1.getGitHubRepoSlug)(gitRemote)
320
536
  : undefined;
321
537
  let contents = await changelogRenderer({
538
+ projectGraph,
322
539
  commits,
323
540
  releaseVersion: releaseVersion.rawVersion,
324
- project: null,
541
+ project: project.name,
325
542
  repoSlug: githubRepoSlug,
326
543
  entryWhenNoChanges: typeof config.entryWhenNoChanges === 'string'
327
544
  ? (0, utils_1.interpolate)(config.entryWhenNoChanges, {
@@ -331,6 +548,7 @@ async function generateChangelogForProjects(tree, rawVersion, dryRun, interactiv
331
548
  })
332
549
  : false,
333
550
  changelogRenderOptions: config.renderOptions,
551
+ conventionalCommitsConfig: nxReleaseConfig.conventionalCommits,
334
552
  });
335
553
  /**
336
554
  * If interactive mode, make the changelog contents available for the user to modify in their editor of choice,
@@ -345,14 +563,10 @@ async function generateChangelogForProjects(tree, rawVersion, dryRun, interactiv
345
563
  await (0, launch_editor_1.launchEditor)(changelogPath);
346
564
  contents = (0, node_fs_1.readFileSync)(changelogPath, 'utf-8');
347
565
  }
348
- /**
349
- * The exact logic we use for printing the summary/diff to the user is dependent upon whether they are creating
350
- * a changelog file, a Github release, or both.
351
- */
352
- let printSummary = () => { };
353
- const noDiffInChangelogMessage = chalk.yellow(`NOTE: There was no diff detected for the changelog entry. Maybe you intended to pass alternative git references via --from and --to?`);
354
566
  if (interpolatedTreePath) {
355
- let changelogContents = tree.read(interpolatedTreePath)?.toString() ?? '';
567
+ let changelogContents = tree.exists(interpolatedTreePath)
568
+ ? tree.read(interpolatedTreePath).toString()
569
+ : '';
356
570
  if (changelogContents) {
357
571
  // NOTE: right now existing releases are always expected to be in markdown format, but in the future we could potentially support others via a custom parser option
358
572
  const changelogReleases = (0, markdown_1.parseChangelogMarkdown)(changelogContents).releases;
@@ -370,74 +584,62 @@ async function generateChangelogForProjects(tree, rawVersion, dryRun, interactiv
370
584
  changelogContents = contents;
371
585
  }
372
586
  tree.write(interpolatedTreePath, changelogContents);
373
- printSummary = () => (0, print_changes_1.printChanges)(tree, !!dryRun, 3, false, noDiffInChangelogMessage,
587
+ (0, print_changes_1.printAndFlushChanges)(tree, !!dryRun, 3, false, shared_1.noDiffInChangelogMessage,
374
588
  // Only print the change for the current changelog file at this point
375
589
  (f) => f.path === interpolatedTreePath);
376
590
  }
377
- if (config.createRelease === 'github') {
378
- if (!githubRepoSlug) {
379
- output_1.output.error({
380
- title: `Unable to create a Github release because the Github repo slug could not be determined.`,
381
- bodyLines: [
382
- `Please ensure you have a valid Github remote configured. You can run \`git remote -v\` to list your current remotes.`,
383
- ],
384
- });
385
- process.exit(1);
386
- }
387
- const token = await (0, github_1.resolveGithubToken)();
388
- const githubRequestConfig = {
389
- repo: githubRepoSlug,
390
- token,
391
- };
392
- let existingGithubReleaseForVersion;
393
- try {
394
- existingGithubReleaseForVersion = await (0, github_1.getGithubReleaseByTag)(githubRequestConfig, releaseVersion.gitTag);
395
- }
396
- catch (err) {
397
- if (err.response?.status === 401) {
398
- output_1.output.error({
399
- title: `Unable to resolve data via the Github API. You can use any of the following options to resolve this:`,
400
- bodyLines: [
401
- '- Set the `GITHUB_TOKEN` or `GH_TOKEN` environment variable to a valid Github token with `repo` scope',
402
- '- Have an active session via the official gh CLI tool (https://cli.github.com) in your current terminal',
403
- ],
404
- });
405
- process.exit(1);
406
- }
407
- if (err.response?.status === 404) {
408
- // No existing release found, this is fine
409
- }
410
- else {
411
- // Rethrow unknown errors for now
412
- throw err;
413
- }
414
- }
415
- let existingPrintSummaryFn = printSummary;
416
- printSummary = () => {
417
- const logTitle = `https://github.com/${githubRepoSlug}/releases/tag/${releaseVersion.gitTag}`;
418
- if (existingGithubReleaseForVersion) {
419
- console.error(`${chalk.white('UPDATE')} ${logTitle}${dryRun ? chalk.keyword('orange')(' [dry-run]') : ''}`);
420
- }
421
- else {
422
- console.error(`${chalk.green('CREATE')} ${logTitle}${dryRun ? chalk.keyword('orange')(' [dry-run]') : ''}`);
423
- }
424
- // Only print the diff here if we are not already going to be printing changes from the Tree
425
- if (!interpolatedTreePath) {
426
- console.log('');
427
- (0, print_changes_1.printDiff)(existingGithubReleaseForVersion
428
- ? existingGithubReleaseForVersion.body
429
- : '', contents, 3, noDiffInChangelogMessage);
430
- }
431
- existingPrintSummaryFn();
432
- };
433
- if (!dryRun) {
434
- await (0, github_1.createOrUpdateGithubRelease)(githubRequestConfig, {
435
- version: releaseVersion.gitTag,
436
- prerelease: releaseVersion.isPrerelease,
437
- body: contents,
438
- }, existingGithubReleaseForVersion);
439
- }
591
+ projectChangelogs[project.name] = {
592
+ releaseVersion,
593
+ contents,
594
+ };
595
+ }
596
+ return projectChangelogs;
597
+ }
598
+ function checkChangelogFilesEnabled(nxReleaseConfig) {
599
+ if (nxReleaseConfig.changelog.workspaceChangelog &&
600
+ nxReleaseConfig.changelog.workspaceChangelog.file) {
601
+ return true;
602
+ }
603
+ for (const releaseGroup of Object.values(nxReleaseConfig.groups)) {
604
+ if (releaseGroup.changelog && releaseGroup.changelog.file) {
605
+ return true;
606
+ }
607
+ }
608
+ return false;
609
+ }
610
+ async function getCommits(fromSHA, toSHA, conventionalCommitsConfig) {
611
+ const rawCommits = await (0, git_1.getGitDiff)(fromSHA, toSHA);
612
+ // Parse as conventional commits
613
+ return (0, git_1.parseCommits)(rawCommits).filter((c) => {
614
+ const type = c.type;
615
+ const typeConfig = conventionalCommitsConfig.types[type];
616
+ if (!typeConfig) {
617
+ // don't include commits with unknown types
618
+ return false;
440
619
  }
441
- printSummary();
620
+ return !typeConfig.changelog.hidden;
621
+ });
622
+ }
623
+ function shouldCreateGitHubRelease(changelogConfig, createReleaseArg = undefined) {
624
+ if (createReleaseArg !== undefined) {
625
+ return createReleaseArg === 'github';
626
+ }
627
+ return (changelogConfig || {}).createRelease === 'github';
628
+ }
629
+ exports.shouldCreateGitHubRelease = shouldCreateGitHubRelease;
630
+ async function promptForGitHubRelease() {
631
+ try {
632
+ const result = await (0, enquirer_1.prompt)([
633
+ {
634
+ name: 'confirmation',
635
+ message: 'Do you want to create a GitHub release anyway?',
636
+ type: 'confirm',
637
+ },
638
+ ]);
639
+ return result.confirmation;
640
+ }
641
+ catch (e) {
642
+ // Handle the case where the user exits the prompt with ctrl+c
643
+ return false;
442
644
  }
443
645
  }