nx 17.2.0-beta.1 → 17.2.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/bin/nx.js +0 -16
  2. package/package.json +12 -13
  3. package/src/command-line/affected/print-affected.js +10 -1
  4. package/src/command-line/graph/graph.js +2 -2
  5. package/src/command-line/init/implementation/add-nx-to-nest.js +1 -1
  6. package/src/command-line/release/changelog.d.ts +7 -1
  7. package/src/command-line/release/changelog.js +153 -86
  8. package/src/command-line/release/command-object.d.ts +9 -4
  9. package/src/command-line/release/command-object.js +14 -3
  10. package/src/command-line/release/config/config.d.ts +10 -2
  11. package/src/command-line/release/config/config.js +24 -3
  12. package/src/command-line/release/index.d.ts +12 -0
  13. package/src/command-line/release/index.js +18 -0
  14. package/src/command-line/release/publish.d.ts +7 -3
  15. package/src/command-line/release/publish.js +23 -8
  16. package/src/command-line/release/utils/git.js +1 -1
  17. package/src/command-line/release/utils/resolve-semver-specifier.js +10 -3
  18. package/src/command-line/release/version.d.ts +22 -1
  19. package/src/command-line/release/version.js +46 -7
  20. package/src/command-line/yargs-utils/shared-options.d.ts +3 -1
  21. package/src/command-line/yargs-utils/shared-options.js +14 -8
  22. package/src/config/nx-json.d.ts +22 -4
  23. package/src/daemon/server/handle-hash-tasks.js +2 -2
  24. package/src/daemon/server/project-graph-incremental-recomputation.d.ts +4 -0
  25. package/src/daemon/server/project-graph-incremental-recomputation.js +23 -18
  26. package/src/executors/run-commands/run-commands.impl.d.ts +1 -0
  27. package/src/executors/run-commands/run-commands.impl.js +9 -8
  28. package/src/executors/run-commands/schema.json +7 -0
  29. package/src/executors/utils/convert-nx-executor.js +1 -1
  30. package/src/hasher/hash-task.js +6 -0
  31. package/src/hasher/native-task-hasher-impl.d.ts +19 -0
  32. package/src/hasher/native-task-hasher-impl.js +35 -0
  33. package/src/hasher/node-task-hasher-impl.d.ts +49 -0
  34. package/src/hasher/node-task-hasher-impl.js +431 -0
  35. package/src/hasher/task-hasher.d.ts +29 -17
  36. package/src/hasher/task-hasher.js +28 -426
  37. package/src/native/index.d.ts +42 -6
  38. package/src/native/index.js +3 -2
  39. package/src/native/transform-objects.js +2 -0
  40. package/src/plugins/js/package-json/create-package-json.js +1 -1
  41. package/src/project-graph/build-project-graph.d.ts +3 -1
  42. package/src/project-graph/build-project-graph.js +5 -1
  43. package/src/project-graph/file-map-utils.d.ts +9 -5
  44. package/src/project-graph/file-map-utils.js +16 -63
  45. package/src/project-graph/project-graph.js +3 -2
  46. package/src/project-graph/utils/build-all-workspace-files.d.ts +2 -0
  47. package/src/project-graph/utils/build-all-workspace-files.js +15 -0
  48. package/src/project-graph/utils/retrieve-workspace-files.d.ts +13 -22
  49. package/src/project-graph/utils/retrieve-workspace-files.js +17 -55
  50. package/src/tasks-runner/run-command.js +2 -6
  51. package/src/utils/nx-plugin.js +12 -2
  52. package/src/utils/workspace-context.d.ts +3 -2
  53. package/src/utils/workspace-context.js +7 -8
package/bin/nx.js CHANGED
@@ -38,22 +38,6 @@ function main() {
38
38
  require('nx/src/command-line/nx-commands').commandsObject.argv;
39
39
  }
40
40
  else {
41
- // v8-compile-cache doesn't support ESM. Attempting to import ESM
42
- // with it enabled results in an error that reads "Invalid host options".
43
- //
44
- // Angular CLI, and prettier both use ESM so we need to disable it in these cases.
45
- if (workspace &&
46
- workspace.type === 'nx' &&
47
- ![
48
- 'format',
49
- 'format:check',
50
- 'format:write',
51
- 'g',
52
- 'generate',
53
- 'release',
54
- ].some((cmd) => process.argv[2] === cmd)) {
55
- require('v8-compile-cache');
56
- }
57
41
  if (!client_1.daemonClient.enabled() && workspace !== null) {
58
42
  (0, workspace_context_1.setupWorkspaceContext)(workspace.dir);
59
43
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nx",
3
- "version": "17.2.0-beta.1",
3
+ "version": "17.2.0-beta.10",
4
4
  "private": false,
5
5
  "description": "The core Nx plugin contains the core functionality of Nx like the project graph, nx commands and task orchestration.",
6
6
  "repository": {
@@ -63,11 +63,10 @@
63
63
  "tmp": "~0.2.1",
64
64
  "tsconfig-paths": "^4.1.2",
65
65
  "tslib": "^2.3.0",
66
- "v8-compile-cache": "2.3.0",
67
66
  "yargs": "^17.6.2",
68
67
  "yargs-parser": "21.1.1",
69
68
  "node-machine-id": "1.1.12",
70
- "@nrwl/tao": "17.2.0-beta.1"
69
+ "@nrwl/tao": "17.2.0-beta.10"
71
70
  },
72
71
  "peerDependencies": {
73
72
  "@swc-node/register": "^1.6.7",
@@ -82,16 +81,16 @@
82
81
  }
83
82
  },
84
83
  "optionalDependencies": {
85
- "@nx/nx-darwin-x64": "17.2.0-beta.1",
86
- "@nx/nx-darwin-arm64": "17.2.0-beta.1",
87
- "@nx/nx-linux-x64-gnu": "17.2.0-beta.1",
88
- "@nx/nx-linux-x64-musl": "17.2.0-beta.1",
89
- "@nx/nx-win32-x64-msvc": "17.2.0-beta.1",
90
- "@nx/nx-linux-arm64-gnu": "17.2.0-beta.1",
91
- "@nx/nx-linux-arm64-musl": "17.2.0-beta.1",
92
- "@nx/nx-linux-arm-gnueabihf": "17.2.0-beta.1",
93
- "@nx/nx-win32-arm64-msvc": "17.2.0-beta.1",
94
- "@nx/nx-freebsd-x64": "17.2.0-beta.1"
84
+ "@nx/nx-darwin-x64": "17.2.0-beta.10",
85
+ "@nx/nx-darwin-arm64": "17.2.0-beta.10",
86
+ "@nx/nx-linux-x64-gnu": "17.2.0-beta.10",
87
+ "@nx/nx-linux-x64-musl": "17.2.0-beta.10",
88
+ "@nx/nx-win32-x64-msvc": "17.2.0-beta.10",
89
+ "@nx/nx-linux-arm64-gnu": "17.2.0-beta.10",
90
+ "@nx/nx-linux-arm64-musl": "17.2.0-beta.10",
91
+ "@nx/nx-linux-arm-gnueabihf": "17.2.0-beta.10",
92
+ "@nx/nx-win32-arm64-msvc": "17.2.0-beta.10",
93
+ "@nx/nx-freebsd-x64": "17.2.0-beta.10"
95
94
  },
96
95
  "nx-migrations": {
97
96
  "migrations": "./migrations.json",
@@ -9,6 +9,8 @@ const package_manager_1 = require("../../utils/package-manager");
9
9
  const command_object_1 = require("./command-object");
10
10
  const logger_1 = require("../../utils/logger");
11
11
  const task_env_1 = require("../../tasks-runner/task-env");
12
+ const build_project_graph_1 = require("../../project-graph/build-project-graph");
13
+ const client_1 = require("../../daemon/client/client");
12
14
  /**
13
15
  * @deprecated Use showProjectsHandler, generateGraph, or affected (without the print-affected mode) instead.
14
16
  */
@@ -35,7 +37,14 @@ exports.printAffected = printAffected;
35
37
  async function createTasks(affectedProjectsWithTargetAndConfig, projectGraph, nxArgs, nxJson, overrides) {
36
38
  const defaultDependencyConfigs = (0, create_task_graph_1.mapTargetDefaultsToDependencies)(nxJson.targetDefaults);
37
39
  const taskGraph = (0, create_task_graph_1.createTaskGraph)(projectGraph, defaultDependencyConfigs, affectedProjectsWithTargetAndConfig.map((p) => p.name), nxArgs.targets, nxArgs.configuration, overrides);
38
- const hasher = new task_hasher_1.InProcessTaskHasher({}, [], projectGraph, nxJson, {});
40
+ let hasher;
41
+ if (client_1.daemonClient.enabled()) {
42
+ hasher = new task_hasher_1.DaemonBasedTaskHasher(client_1.daemonClient, {});
43
+ }
44
+ else {
45
+ const { fileMap, allWorkspaceFiles, rustReferences } = (0, build_project_graph_1.getFileMap)();
46
+ hasher = new task_hasher_1.InProcessTaskHasher(fileMap?.projectFileMap, allWorkspaceFiles, projectGraph, nxJson, rustReferences, {});
47
+ }
39
48
  const execCommand = (0, package_manager_1.getPackageManagerCommand)().exec;
40
49
  const tasks = Object.values(taskGraph.tasks);
41
50
  await Promise.all(tasks.map((t) => (0, hash_task_1.hashTask)(hasher, projectGraph, taskGraph, t,
@@ -15,7 +15,6 @@ const fileutils_1 = require("../../utils/fileutils");
15
15
  const output_1 = require("../../utils/output");
16
16
  const workspace_root_1 = require("../../utils/workspace-root");
17
17
  const client_1 = require("../../daemon/client/client");
18
- const task_hasher_1 = require("../../hasher/task-hasher");
19
18
  const typescript_1 = require("../../plugins/js/utils/typescript");
20
19
  const operators_1 = require("../../project-graph/operators");
21
20
  const project_graph_1 = require("../../project-graph/project-graph");
@@ -26,6 +25,7 @@ const native_1 = require("../../native");
26
25
  const transform_objects_1 = require("../../native/transform-objects");
27
26
  const affected_1 = require("../affected/affected");
28
27
  const nx_deps_cache_1 = require("../../project-graph/nx-deps-cache");
28
+ const task_hasher_1 = require("../../hasher/task-hasher");
29
29
  // maps file extention to MIME types
30
30
  const mimeType = {
31
31
  '.ico': 'image/x-icon',
@@ -435,7 +435,7 @@ async function createTaskGraphClientResponse(pruneExternal = false) {
435
435
  perf_hooks_1.performance.mark('task graph generation:start');
436
436
  const taskGraphs = getAllTaskGraphsForWorkspace(nxJson, graph);
437
437
  perf_hooks_1.performance.mark('task graph generation:end');
438
- const planner = new native_1.HashPlanner(workspace_root_1.workspaceRoot, nxJson, (0, transform_objects_1.transformProjectGraphForRust)(graph));
438
+ const planner = new native_1.HashPlanner(nxJson, (0, native_1.transferProjectGraph)((0, transform_objects_1.transformProjectGraphForRust)(graph)));
439
439
  perf_hooks_1.performance.mark('task hash plan generation:start');
440
440
  const plans = {};
441
441
  for (const individualTaskGraph of Object.values(taskGraphs.taskGraphs)) {
@@ -173,7 +173,7 @@ function createProjectJson(repoRoot, packageJson, nestCLIOptions) {
173
173
  executor: '@nx/eslint:lint',
174
174
  outputs: ['{options.outputFile}'],
175
175
  options: {
176
- lintFilePatterns: ['src/**/*.ts', 'test/**/*.ts'],
176
+ lintFilePatterns: ['./src', './test'],
177
177
  },
178
178
  };
179
179
  // test and e2e
@@ -1,2 +1,8 @@
1
1
  import { ChangelogOptions } from './command-object';
2
- export declare function changelogHandler(args: ChangelogOptions): Promise<number>;
2
+ export declare const releaseChangelogCLIHandler: (args: ChangelogOptions) => Promise<any>;
3
+ /**
4
+ * NOTE: This function is also exported for programmatic usage and forms part of the public API
5
+ * of Nx. We intentionally do not wrap the implementation with handleErrors because users need
6
+ * to have control over their own error handling when using the API.
7
+ */
8
+ export declare function releaseChangelog(args: ChangelogOptions): Promise<number>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.changelogHandler = void 0;
3
+ exports.releaseChangelog = exports.releaseChangelogCLIHandler = void 0;
4
4
  const chalk = require("chalk");
5
5
  const node_fs_1 = require("node:fs");
6
6
  const semver_1 = require("semver");
@@ -24,103 +24,158 @@ const launch_editor_1 = require("./utils/launch-editor");
24
24
  const markdown_1 = require("./utils/markdown");
25
25
  const print_changes_1 = require("./utils/print-changes");
26
26
  const shared_1 = require("./utils/shared");
27
- async function changelogHandler(args) {
28
- return (0, params_1.handleErrors)(args.verbose, async () => {
29
- // Right now, the given version must be valid semver in order to proceed
30
- if (!(0, semver_1.valid)(args.version)) {
31
- 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`);
32
- }
33
- const projectGraph = await (0, project_graph_1.createProjectGraphAsync)({ exitOnError: true });
34
- const nxJson = (0, nx_json_1.readNxJson)();
35
- if (args.verbose) {
36
- process.env.NX_VERBOSE_LOGGING = 'true';
37
- }
38
- // Apply default configuration to any optional user configuration
39
- const { error: configError, nxReleaseConfig } = await (0, config_1.createNxReleaseConfig)(projectGraph, nxJson.release);
40
- if (configError) {
41
- return await (0, config_1.handleNxReleaseConfigError)(configError);
27
+ const releaseChangelogCLIHandler = (args) => (0, params_1.handleErrors)(args.verbose, () => releaseChangelog(args));
28
+ exports.releaseChangelogCLIHandler = releaseChangelogCLIHandler;
29
+ /**
30
+ * NOTE: This function is also exported for programmatic usage and forms part of the public API
31
+ * of Nx. We intentionally do not wrap the implementation with handleErrors because users need
32
+ * to have control over their own error handling when using the API.
33
+ */
34
+ async function releaseChangelog(args) {
35
+ const projectGraph = await (0, project_graph_1.createProjectGraphAsync)({ exitOnError: true });
36
+ const nxJson = (0, nx_json_1.readNxJson)();
37
+ if (args.verbose) {
38
+ process.env.NX_VERBOSE_LOGGING = 'true';
39
+ }
40
+ // Apply default configuration to any optional user configuration
41
+ const { error: configError, nxReleaseConfig } = await (0, config_1.createNxReleaseConfig)(projectGraph, nxJson.release);
42
+ if (configError) {
43
+ return await (0, config_1.handleNxReleaseConfigError)(configError);
44
+ }
45
+ const { error: filterError, releaseGroups, releaseGroupToFilteredProjects, } = (0, filter_release_groups_1.filterReleaseGroups)(projectGraph, nxReleaseConfig, args.projects, args.groups);
46
+ if (filterError) {
47
+ output_1.output.error(filterError);
48
+ process.exit(1);
49
+ }
50
+ /**
51
+ * For determining the versions to use within changelog files, there are a few different possibilities:
52
+ * - the user is using the nx CLI, and therefore passes a single --version argument which represents the version for any and all changelog
53
+ * files which will be generated (i.e. both the workspace changelog, and all project changelogs, depending on which of those has been enabled)
54
+ * - the user is using the nxReleaseChangelog API programmatically, and:
55
+ * - passes only a version property
56
+ * - this works in the same way as described above for the CLI
57
+ * - passes only a versionData object
58
+ * - this is a special case where the user is providing a version for each project, and therefore the version argument is not needed
59
+ * - NOTE: it is not possible to generate a workspace level changelog with only a versionData object, and this will produce an error
60
+ * - passes both a version and a versionData object
61
+ * - in this case, the version property will be used as the reference for the workspace changelog, and the versionData object will be used
62
+ * to generate project changelogs
63
+ */
64
+ const { workspaceChangelogVersion, projectsVersionData } = resolveChangelogVersions(args, releaseGroups, releaseGroupToFilteredProjects);
65
+ const to = args.to || 'HEAD';
66
+ const toSHA = await (0, git_1.getCommitHash)(to);
67
+ const headSHA = to === 'HEAD' ? toSHA : await (0, git_1.getCommitHash)('HEAD');
68
+ /**
69
+ * Protect the user against attempting to create a new commit when recreating an old release changelog,
70
+ * this seems like it would always be unintentional.
71
+ */
72
+ const autoCommitEnabled = args.gitCommit ?? nxReleaseConfig.changelog.git.commit;
73
+ if (autoCommitEnabled && headSHA !== toSHA) {
74
+ 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`);
75
+ }
76
+ const fromRef = args.from ||
77
+ (await (0, git_1.getLatestGitTagForPattern)(nxReleaseConfig.releaseTagPattern))?.tag;
78
+ if (!fromRef) {
79
+ throw new Error(`Unable to determine the previous git tag, please provide an explicit git reference using --from`);
80
+ }
81
+ // Make sure that the fromRef is actually resolvable
82
+ const fromSHA = await (0, git_1.getCommitHash)(fromRef);
83
+ const rawCommits = await (0, git_1.getGitDiff)(fromSHA, toSHA);
84
+ // Parse as conventional commits
85
+ const commits = (0, git_1.parseCommits)(rawCommits).filter((c) => {
86
+ const type = c.type;
87
+ // Always ignore non user-facing commits for now
88
+ // TODO: allow this filter to be configurable via config in a future release
89
+ if (type === 'feat' || type === 'fix' || type === 'perf') {
90
+ return true;
42
91
  }
43
- const toSHA = await (0, git_1.getCommitHash)(args.to);
44
- const headSHA = args.to === 'HEAD' ? toSHA : await (0, git_1.getCommitHash)('HEAD');
92
+ return false;
93
+ });
94
+ const tree = new tree_1.FsTree(workspace_root_1.workspaceRoot, args.verbose);
95
+ const userCommitMessage = args.gitCommitMessage || nxReleaseConfig.changelog.git.commitMessage;
96
+ const commitMessageValues = (0, shared_1.createCommitMessageValues)(releaseGroups, releaseGroupToFilteredProjects, projectsVersionData, userCommitMessage);
97
+ // 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
98
+ const gitTagValues = args.gitTag ?? nxReleaseConfig.changelog.git.tag
99
+ ? (0, shared_1.createGitTagValues)(releaseGroups, releaseGroupToFilteredProjects, projectsVersionData)
100
+ : [];
101
+ (0, shared_1.handleDuplicateGitTags)(gitTagValues);
102
+ const postGitTasks = [];
103
+ await generateChangelogForWorkspace(tree, args, nxReleaseConfig, workspaceChangelogVersion, commits, postGitTasks);
104
+ if (args.projects?.length) {
45
105
  /**
46
- * Protect the user against attempting to create a new commit when recreating an old release changelog,
47
- * this seems like it would always be unintentional.
106
+ * Run changelog generation for all remaining release groups and filtered projects within them
48
107
  */
49
- const autoCommitEnabled = args.gitCommit ?? nxReleaseConfig.changelog.git.commit;
50
- if (autoCommitEnabled && headSHA !== toSHA) {
51
- 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`);
52
- }
53
- const { error: filterError, releaseGroups, releaseGroupToFilteredProjects, } = (0, filter_release_groups_1.filterReleaseGroups)(projectGraph, nxReleaseConfig, args.projects, args.groups);
54
- if (filterError) {
55
- output_1.output.error(filterError);
56
- process.exit(1);
57
- }
58
- const fromRef = args.from ||
59
- (await (0, git_1.getLatestGitTagForPattern)(nxReleaseConfig.releaseTagPattern))?.tag;
60
- if (!fromRef) {
61
- throw new Error(`Unable to determine the previous git tag, please provide an explicit git reference using --from`);
108
+ for (const releaseGroup of releaseGroups) {
109
+ const projectNodes = Array.from(releaseGroupToFilteredProjects.get(releaseGroup)).map((name) => projectGraph.nodes[name]);
110
+ await generateChangelogForProjects(tree, args, commits, projectsVersionData, postGitTasks, releaseGroup, projectNodes);
62
111
  }
63
- // Make sure that the fromRef is actually resolvable
64
- const fromSHA = await (0, git_1.getCommitHash)(fromRef);
65
- const rawCommits = await (0, git_1.getGitDiff)(fromSHA, toSHA);
66
- // Parse as conventional commits
67
- const commits = (0, git_1.parseCommits)(rawCommits).filter((c) => {
68
- const type = c.type;
69
- // Always ignore non user-facing commits for now
70
- // TODO: allow this filter to be configurable via config in a future release
71
- if (type === 'feat' || type === 'fix' || type === 'perf') {
72
- return true;
73
- }
74
- return false;
75
- });
76
- const tree = new tree_1.FsTree(workspace_root_1.workspaceRoot, args.verbose);
77
- // Create a pseudo-versionData object using the version passed into the command so that we can share commit and tagging utils with version
78
- const versionData = releaseGroups.reduce((versionData, releaseGroup) => {
79
- const releaseGroupProjectNames = Array.from(releaseGroupToFilteredProjects.get(releaseGroup));
80
- for (const projectName of releaseGroupProjectNames) {
112
+ return await applyChangesAndExit(args, nxReleaseConfig, tree, toSHA, postGitTasks, commitMessageValues, gitTagValues);
113
+ }
114
+ /**
115
+ * Run changelog generation for all remaining release groups
116
+ */
117
+ for (const releaseGroup of releaseGroups) {
118
+ const projectNodes = releaseGroup.projects.map((name) => projectGraph.nodes[name]);
119
+ await generateChangelogForProjects(tree, args, commits, projectsVersionData, postGitTasks, releaseGroup, projectNodes);
120
+ }
121
+ return await applyChangesAndExit(args, nxReleaseConfig, tree, toSHA, postGitTasks, commitMessageValues, gitTagValues);
122
+ }
123
+ exports.releaseChangelog = releaseChangelog;
124
+ function resolveChangelogVersions(args, releaseGroups, releaseGroupToFilteredProjects) {
125
+ if (!args.version && !args.versionData) {
126
+ throw new Error(`You must provide a version string and/or a versionData object.`);
127
+ }
128
+ /**
129
+ * TODO: revaluate this assumption holistically in a dedicated PR when we add support for calver
130
+ * (e.g. the Release class also uses semver utils to check if prerelease).
131
+ *
132
+ * Right now, the given version must be valid semver in order to proceed
133
+ */
134
+ if (args.version && !(0, semver_1.valid)(args.version)) {
135
+ 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`);
136
+ }
137
+ const versionData = releaseGroups.reduce((versionData, releaseGroup) => {
138
+ const releaseGroupProjectNames = Array.from(releaseGroupToFilteredProjects.get(releaseGroup));
139
+ for (const projectName of releaseGroupProjectNames) {
140
+ if (!args.versionData) {
81
141
  versionData[projectName] = {
82
142
  newVersion: args.version,
83
143
  currentVersion: '',
84
- dependentProjects: [], // not needed within changelog/commit generation
144
+ dependentProjects: [], // not relevant within changelog/commit generation
85
145
  };
146
+ continue;
86
147
  }
87
- return versionData;
88
- }, {});
89
- const userCommitMessage = args.gitCommitMessage || nxReleaseConfig.changelog.git.commitMessage;
90
- const commitMessageValues = (0, shared_1.createCommitMessageValues)(releaseGroups, releaseGroupToFilteredProjects, versionData, userCommitMessage);
91
- // 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
92
- const gitTagValues = args.gitTag ?? nxReleaseConfig.changelog.git.tag
93
- ? (0, shared_1.createGitTagValues)(releaseGroups, releaseGroupToFilteredProjects, versionData)
94
- : [];
95
- (0, shared_1.handleDuplicateGitTags)(gitTagValues);
96
- const postGitTasks = [];
97
- await generateChangelogForWorkspace(tree, args, nxReleaseConfig, commits, postGitTasks);
98
- if (args.projects?.length) {
99
148
  /**
100
- * Run changelog generation for all remaining release groups and filtered projects within them
149
+ * In the case where a versionData object was provided, we need to make sure all projects are present,
150
+ * otherwise it suggests a filtering mismatch between the version and changelog command invocations.
101
151
  */
102
- for (const releaseGroup of releaseGroups) {
103
- const projectNodes = Array.from(releaseGroupToFilteredProjects.get(releaseGroup)).map((name) => projectGraph.nodes[name]);
104
- await generateChangelogForProjects(tree, args, commits, postGitTasks, releaseGroup, projectNodes);
152
+ if (!args.versionData[projectName]) {
153
+ 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.`);
105
154
  }
106
- return await applyChangesAndExit(args, nxReleaseConfig, tree, toSHA, postGitTasks, commitMessageValues, gitTagValues);
107
- }
108
- /**
109
- * Run changelog generation for all remaining release groups
110
- */
111
- for (const releaseGroup of releaseGroups) {
112
- const projectNodes = releaseGroup.projects.map((name) => projectGraph.nodes[name]);
113
- await generateChangelogForProjects(tree, args, commits, postGitTasks, releaseGroup, projectNodes);
114
155
  }
115
- return await applyChangesAndExit(args, nxReleaseConfig, tree, toSHA, postGitTasks, commitMessageValues, gitTagValues);
116
- });
156
+ return versionData;
157
+ }, args.versionData || {});
158
+ return {
159
+ workspaceChangelogVersion: args.version,
160
+ projectsVersionData: versionData,
161
+ };
117
162
  }
118
- exports.changelogHandler = changelogHandler;
119
163
  async function applyChangesAndExit(args, nxReleaseConfig, tree, toSHA, postGitTasks, commitMessageValues, gitTagValues) {
120
164
  let latestCommit = toSHA;
165
+ const changes = tree.listChanges();
166
+ // This could happen we using conventional commits, for example
167
+ if (!changes.length) {
168
+ output_1.output.warn({
169
+ title: `No changes detected for changelogs`,
170
+ bodyLines: [
171
+ `No changes were detected for any changelog files, so no changelog entries will be generated.`,
172
+ ],
173
+ });
174
+ return 0;
175
+ }
121
176
  // Generate a new commit for the changes, if configured to do so
122
177
  if (args.gitCommit ?? nxReleaseConfig.changelog.git.commit) {
123
- await (0, shared_1.commitChanges)(tree.listChanges().map((f) => f.path), !!args.dryRun, !!args.verbose, commitMessageValues, args.gitCommitArgs || nxReleaseConfig.changelog.git.commitArgs);
178
+ await (0, shared_1.commitChanges)(changes.map((f) => f.path), !!args.dryRun, !!args.verbose, commitMessageValues, args.gitCommitArgs || nxReleaseConfig.changelog.git.commitArgs);
124
179
  // Resolve the commit we just made
125
180
  latestCommit = await (0, git_1.getCommitHash)('HEAD');
126
181
  }
@@ -165,16 +220,22 @@ function resolveChangelogRenderer(changelogRendererPath) {
165
220
  }
166
221
  return changelogRenderer;
167
222
  }
168
- async function generateChangelogForWorkspace(tree, args, nxReleaseConfig, commits, postGitTasks) {
223
+ async function generateChangelogForWorkspace(tree, args, nxReleaseConfig, workspaceChangelogVersion, commits, postGitTasks) {
169
224
  const config = nxReleaseConfig.changelog.workspaceChangelog;
170
225
  // The entire feature is disabled at the workspace level, exit early
171
226
  if (config === false) {
172
227
  return;
173
228
  }
229
+ // If explicitly null it must mean that no changes were detected (e.g. when using conventional commits), so do nothing
230
+ if (workspaceChangelogVersion === null) {
231
+ return;
232
+ }
233
+ if (!workspaceChangelogVersion) {
234
+ throw new Error(`Workspace changelog is enabled but no overall version was provided. Please provide an explicit version using --version`);
235
+ }
174
236
  // Only trigger interactive mode for the workspace changelog if the user explicitly requested it via "all" or "workspace"
175
237
  const interactive = args.interactive === 'all' || args.interactive === 'workspace';
176
238
  const dryRun = !!args.dryRun;
177
- const verbose = !!args.verbose;
178
239
  const gitRemote = args.gitRemote;
179
240
  const changelogRenderer = resolveChangelogRenderer(config.renderer);
180
241
  let interpolatedTreePath = config.file || '';
@@ -186,7 +247,7 @@ async function generateChangelogForWorkspace(tree, args, nxReleaseConfig, commit
186
247
  });
187
248
  }
188
249
  const releaseVersion = new shared_1.ReleaseVersion({
189
- version: args.version,
250
+ version: workspaceChangelogVersion,
190
251
  releaseTagPattern: nxReleaseConfig.releaseTagPattern,
191
252
  });
192
253
  // We are either creating/previewing a changelog file, a Github release, or both
@@ -329,7 +390,7 @@ async function generateChangelogForWorkspace(tree, args, nxReleaseConfig, commit
329
390
  }
330
391
  printSummary();
331
392
  }
332
- async function generateChangelogForProjects(tree, args, commits, postGitTasks, releaseGroup, projects) {
393
+ async function generateChangelogForProjects(tree, args, commits, projectsVersionData, postGitTasks, releaseGroup, projects) {
333
394
  const config = releaseGroup.changelog;
334
395
  // The entire feature is disabled at the release group level, exit early
335
396
  if (config === false) {
@@ -339,7 +400,6 @@ async function generateChangelogForProjects(tree, args, commits, postGitTasks, r
339
400
  const interactive = args.interactive === 'all' || args.interactive === 'projects';
340
401
  const dryRun = !!args.dryRun;
341
402
  const gitRemote = args.gitRemote;
342
- const rawVersion = args.version;
343
403
  const changelogRenderer = resolveChangelogRenderer(config.renderer);
344
404
  for (const project of projects) {
345
405
  let interpolatedTreePath = config.file || '';
@@ -350,8 +410,15 @@ async function generateChangelogForProjects(tree, args, commits, postGitTasks, r
350
410
  workspaceRoot: '', // within the tree, workspaceRoot is the root
351
411
  });
352
412
  }
413
+ /**
414
+ * newVersion will be null in the case that no changes were detected (e.g. in conventional commits mode),
415
+ * no changelog entry is relevant in that case.
416
+ */
417
+ if (projectsVersionData[project.name].newVersion === null) {
418
+ continue;
419
+ }
353
420
  const releaseVersion = new shared_1.ReleaseVersion({
354
- version: rawVersion,
421
+ version: projectsVersionData[project.name].newVersion,
355
422
  releaseTagPattern: releaseGroup.releaseTagPattern,
356
423
  projectName: project.name,
357
424
  });
@@ -1,5 +1,6 @@
1
1
  import { CommandModule } from 'yargs';
2
- import { RunManyOptions } from '../yargs-utils/shared-options';
2
+ import { OutputStyle, RunManyOptions } from '../yargs-utils/shared-options';
3
+ import { VersionData } from './utils/shared';
3
4
  export interface NxReleaseArgs {
4
5
  groups?: string[];
5
6
  projects?: string[];
@@ -17,15 +18,19 @@ interface GitCommitAndTagOptions {
17
18
  export type VersionOptions = NxReleaseArgs & GitCommitAndTagOptions & {
18
19
  specifier?: string;
19
20
  preid?: string;
21
+ stageChanges?: boolean;
20
22
  };
21
23
  export type ChangelogOptions = NxReleaseArgs & GitCommitAndTagOptions & {
22
- version: string;
23
- to: string;
24
+ version?: string | null;
25
+ versionData?: VersionData;
26
+ to?: string;
24
27
  from?: string;
25
28
  interactive?: string;
26
29
  gitRemote?: string;
27
30
  };
28
- export type PublishOptions = NxReleaseArgs & RunManyOptions & {
31
+ export type PublishOptions = NxReleaseArgs & Partial<RunManyOptions> & {
32
+ outputStyle?: OutputStyle;
33
+ } & {
29
34
  registry?: string;
30
35
  tag?: string;
31
36
  otp?: number;
@@ -68,8 +68,19 @@ const versionCommand = {
68
68
  type: 'string',
69
69
  describe: 'The optional prerelease identifier to apply to the version, in the case that specifier has been set to prerelease.',
70
70
  default: '',
71
+ })
72
+ .option('stageChanges', {
73
+ type: 'boolean',
74
+ describe: 'Whether or not to stage the changes made by this command, irrespective of the git config in nx.json related to automated commits. Useful when combining this command with changelog generation.',
75
+ default: false,
71
76
  })),
72
- handler: (args) => Promise.resolve().then(() => require('./version')).then((m) => m.versionHandler(args)),
77
+ handler: (args) => Promise.resolve().then(() => require('./version')).then((m) => m.releaseVersionCLIHandler(args))
78
+ .then((versionDataOrExitCode) => {
79
+ if (typeof versionDataOrExitCode === 'number') {
80
+ return process.exit(versionDataOrExitCode);
81
+ }
82
+ process.exit(0);
83
+ }),
73
84
  };
74
85
  const changelogCommand = {
75
86
  command: 'changelog [version]',
@@ -109,7 +120,7 @@ const changelogCommand = {
109
120
  return true;
110
121
  })),
111
122
  handler: async (args) => {
112
- const status = await (await Promise.resolve().then(() => require('./changelog'))).changelogHandler(args);
123
+ const status = await (await Promise.resolve().then(() => require('./changelog'))).releaseChangelogCLIHandler(args);
113
124
  process.exit(status);
114
125
  },
115
126
  };
@@ -130,7 +141,7 @@ const publishCommand = {
130
141
  type: 'number',
131
142
  description: 'A one-time password for publishing to a registry that requires 2FA',
132
143
  }),
133
- handler: (args) => Promise.resolve().then(() => require('./publish')).then((m) => m.publishHandler(coerceParallelOption((0, shared_options_1.withOverrides)(args, 2)))),
144
+ handler: (args) => Promise.resolve().then(() => require('./publish')).then((m) => m.releasePublishCLIHandler(coerceParallelOption((0, shared_options_1.withOverrides)(args, 2)))),
134
145
  };
135
146
  function coerceParallelOption(args) {
136
147
  if (args['parallel'] === 'false' || args['parallel'] === false) {
@@ -23,6 +23,13 @@ type EnsureProjectsArray<T> = {
23
23
  projects: string[];
24
24
  } : T[K];
25
25
  };
26
+ type RemoveTrueFromType<T> = T extends true ? never : T;
27
+ type RemoveTrueFromProperties<T, K extends keyof T> = {
28
+ [P in keyof T]: P extends K ? RemoveTrueFromType<T[P]> : T[P];
29
+ };
30
+ type RemoveTrueFromPropertiesOnEach<T, K extends keyof T[keyof T]> = {
31
+ [U in keyof T]: RemoveTrueFromProperties<T[U], K>;
32
+ };
26
33
  export declare const CATCH_ALL_RELEASE_GROUP = "__default__";
27
34
  /**
28
35
  * Our source of truth is a deeply required variant of the user-facing config interface, so that command
@@ -34,7 +41,8 @@ export declare const CATCH_ALL_RELEASE_GROUP = "__default__";
34
41
  * pattern such as directories and globs).
35
42
  */
36
43
  export type NxReleaseConfig = DeepRequired<NxJsonConfiguration['release'] & {
37
- groups: DeepRequired<EnsureProjectsArray<NxJsonConfiguration['release']['groups']>>;
44
+ groups: DeepRequired<RemoveTrueFromPropertiesOnEach<EnsureProjectsArray<NxJsonConfiguration['release']['groups']>, 'changelog'>>;
45
+ changelog: RemoveTrueFromProperties<DeepRequired<NxJsonConfiguration['release']['changelog']>, 'workspaceChangelog' | 'projectChangelogs'>;
38
46
  }>;
39
47
  export interface CreateNxReleaseConfigError {
40
48
  code: 'RELEASE_GROUP_MATCHES_NO_PROJECTS' | 'RELEASE_GROUP_RELEASE_TAG_PATTERN_VERSION_PLACEHOLDER_MISSING_OR_EXCESSIVE' | 'PROJECT_MATCHES_MULTIPLE_GROUPS' | 'PROJECTS_MISSING_TARGET';
@@ -44,5 +52,5 @@ export declare function createNxReleaseConfig(projectGraph: ProjectGraph, userCo
44
52
  error: null | CreateNxReleaseConfigError;
45
53
  nxReleaseConfig: NxReleaseConfig | null;
46
54
  }>;
47
- export declare function handleNxReleaseConfigError(error: CreateNxReleaseConfigError): Promise<void>;
55
+ export declare function handleNxReleaseConfigError(error: CreateNxReleaseConfigError): Promise<never>;
48
56
  export {};
@@ -92,15 +92,25 @@ requiredTargetName) {
92
92
  // Merge in the git defaults from the top level
93
93
  { git: rootGitConfig },
94
94
  ], userConfig.version);
95
+ if (userConfig.changelog?.workspaceChangelog) {
96
+ userConfig.changelog.workspaceChangelog = normalizeTrueToEmptyObject(userConfig.changelog.workspaceChangelog);
97
+ }
98
+ if (userConfig.changelog?.projectChangelogs) {
99
+ userConfig.changelog.projectChangelogs = normalizeTrueToEmptyObject(userConfig.changelog.projectChangelogs);
100
+ }
95
101
  const rootChangelogConfig = deepMergeDefaults([
96
102
  WORKSPACE_DEFAULTS.changelog,
97
103
  // Merge in the git defaults from the top level
98
104
  { git: rootGitConfig },
99
- ], userConfig.changelog);
105
+ ], normalizeTrueToEmptyObject(userConfig.changelog));
100
106
  // git configuration is not supported at the group level, only the root/command level
101
107
  const rootVersionWithoutGit = { ...rootVersionConfig };
102
108
  delete rootVersionWithoutGit.git;
103
- const allProjects = (0, find_matching_projects_1.findMatchingProjects)(['*'], projectGraph.nodes);
109
+ const allProjects = (0, find_matching_projects_1.findMatchingProjects)(['*'], projectGraph.nodes).filter(
110
+ // only include libs by default when the user has no groups config,
111
+ // because the default implementation assumes npm js packages
112
+ // and these will usually be libs
113
+ (project) => projectGraph.nodes[project].type === 'lib');
104
114
  const groups = userConfig.groups && Object.keys(userConfig.groups).length
105
115
  ? ensureProjectsConfigIsArray(userConfig.groups)
106
116
  : /**
@@ -183,6 +193,9 @@ requiredTargetName) {
183
193
  groupChangelogDefaults.push(rootChangelogConfig.projectChangelogs);
184
194
  }
185
195
  const projectsRelationship = releaseGroup.projectsRelationship || GROUP_DEFAULTS.projectsRelationship;
196
+ if (releaseGroup.changelog) {
197
+ releaseGroup.changelog = normalizeTrueToEmptyObject(releaseGroup.changelog);
198
+ }
186
199
  const groupDefaults = {
187
200
  projectsRelationship,
188
201
  projects: matchingProjects,
@@ -197,7 +210,7 @@ requiredTargetName) {
197
210
  // The appropriate group default releaseTagPattern is dependent upon the projectRelationships
198
211
  (projectsRelationship === 'independent'
199
212
  ? defaultIndependentReleaseTagPattern
200
- : defaultFixedReleaseTagPattern),
213
+ : userConfig.releaseTagPattern || defaultFixedReleaseTagPattern),
201
214
  };
202
215
  releaseGroups[releaseGroupName] = deepMergeDefaults([groupDefaults], {
203
216
  ...releaseGroup,
@@ -218,6 +231,14 @@ requiredTargetName) {
218
231
  };
219
232
  }
220
233
  exports.createNxReleaseConfig = createNxReleaseConfig;
234
+ /**
235
+ * In some cases it is much cleaner and more intuitive for the user to be able to
236
+ * specify `true` in their config when they want to use the default config for a
237
+ * particular property, rather than having to specify an empty object.
238
+ */
239
+ function normalizeTrueToEmptyObject(value) {
240
+ return value === true ? {} : value;
241
+ }
221
242
  async function handleNxReleaseConfigError(error) {
222
243
  switch (error.code) {
223
244
  case 'RELEASE_GROUP_MATCHES_NO_PROJECTS':
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @public
3
+ */
4
+ export { releaseChangelog } from './changelog';
5
+ /**
6
+ * @public
7
+ */
8
+ export { releasePublish } from './publish';
9
+ /**
10
+ * @public
11
+ */
12
+ export { releaseVersion } from './version';