nx 20.0.0-beta.6 → 20.0.0-beta.7

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nx",
3
- "version": "20.0.0-beta.6",
3
+ "version": "20.0.0-beta.7",
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": {
@@ -80,16 +80,16 @@
80
80
  }
81
81
  },
82
82
  "optionalDependencies": {
83
- "@nx/nx-darwin-x64": "20.0.0-beta.6",
84
- "@nx/nx-darwin-arm64": "20.0.0-beta.6",
85
- "@nx/nx-linux-x64-gnu": "20.0.0-beta.6",
86
- "@nx/nx-linux-x64-musl": "20.0.0-beta.6",
87
- "@nx/nx-win32-x64-msvc": "20.0.0-beta.6",
88
- "@nx/nx-linux-arm64-gnu": "20.0.0-beta.6",
89
- "@nx/nx-linux-arm64-musl": "20.0.0-beta.6",
90
- "@nx/nx-linux-arm-gnueabihf": "20.0.0-beta.6",
91
- "@nx/nx-win32-arm64-msvc": "20.0.0-beta.6",
92
- "@nx/nx-freebsd-x64": "20.0.0-beta.6"
83
+ "@nx/nx-darwin-x64": "20.0.0-beta.7",
84
+ "@nx/nx-darwin-arm64": "20.0.0-beta.7",
85
+ "@nx/nx-linux-x64-gnu": "20.0.0-beta.7",
86
+ "@nx/nx-linux-x64-musl": "20.0.0-beta.7",
87
+ "@nx/nx-win32-x64-msvc": "20.0.0-beta.7",
88
+ "@nx/nx-linux-arm64-gnu": "20.0.0-beta.7",
89
+ "@nx/nx-linux-arm64-musl": "20.0.0-beta.7",
90
+ "@nx/nx-linux-arm-gnueabihf": "20.0.0-beta.7",
91
+ "@nx/nx-win32-arm64-msvc": "20.0.0-beta.7",
92
+ "@nx/nx-freebsd-x64": "20.0.0-beta.7"
93
93
  },
94
94
  "nx-migrations": {
95
95
  "migrations": "./migrations.json",
@@ -1,15 +1,13 @@
1
1
  import { ChangelogChange } from '../../src/command-line/release/changelog';
2
2
  import { NxReleaseConfig } from '../../src/command-line/release/config/config';
3
- import { GitCommit } from '../../src/command-line/release/utils/git';
4
- import { GithubRepoData, RepoSlug } from '../../src/command-line/release/utils/github';
5
- import type { ProjectGraph } from '../../src/config/project-graph';
3
+ import { GithubRepoData } from '../../src/command-line/release/utils/github';
6
4
  /**
7
5
  * The ChangelogRenderOptions are specific to each ChangelogRenderer implementation, and are taken
8
6
  * from the user's nx.json configuration and passed as is into the ChangelogRenderer function.
9
7
  */
10
8
  export type ChangelogRenderOptions = Record<string, unknown>;
11
9
  /**
12
- * When versioning projects independently and enabling `"updateDependents": "always"`, there could
10
+ * When versioning projects independently and enabling `"updateDependents": "auto"`, there could
13
11
  * be additional dependency bump information that is not captured in the commit data, but that nevertheless
14
12
  * should be included in the rendered changelog.
15
13
  */
@@ -17,34 +15,6 @@ export type DependencyBump = {
17
15
  dependencyName: string;
18
16
  newVersion: string;
19
17
  };
20
- /**
21
- * A ChangelogRenderer function takes in the extracted commits and other relevant metadata
22
- * and returns a string, or a Promise of a string of changelog contents (usually markdown).
23
- *
24
- * @param {Object} config The configuration object for the ChangelogRenderer
25
- * @param {ProjectGraph} config.projectGraph The project graph for the workspace
26
- * @param {GitCommit[]} config.commits DEPRECATED [Use 'config.changes' instead] - The collection of extracted commits to generate a changelog for
27
- * @param {ChangelogChange[]} config.changes The collection of changes to show in the changelog
28
- * @param {string} config.releaseVersion The version that is being released
29
- * @param {string | null} config.project The name of specific project to generate a changelog for, or `null` if the overall workspace changelog
30
- * @param {string | false} config.entryWhenNoChanges The (already interpolated) string to use as the changelog entry when there are no changes, or `false` if no entry should be generated
31
- * @param {ChangelogRenderOptions} config.changelogRenderOptions The options specific to the ChangelogRenderer implementation
32
- * @param {DependencyBump[]} config.dependencyBumps Optional list of additional dependency bumps that occurred as part of the release, outside of the commit data
33
- * @param {GithubRepoData} config.repoData Resolved data for the current GitHub repository
34
- */
35
- export type ChangelogRenderer = (config: {
36
- projectGraph: ProjectGraph;
37
- commits?: GitCommit[];
38
- changes?: ChangelogChange[];
39
- releaseVersion: string;
40
- project: string | null;
41
- entryWhenNoChanges: string | false;
42
- changelogRenderOptions: DefaultChangelogRenderOptions;
43
- dependencyBumps?: DependencyBump[];
44
- repoSlug?: RepoSlug;
45
- repoData?: GithubRepoData;
46
- conventionalCommitsConfig: NxReleaseConfig['conventionalCommits'] | null;
47
- }) => Promise<string> | string;
48
18
  /**
49
19
  * The specific options available to the default implementation of the ChangelogRenderer that nx exports
50
20
  * for the common case.
@@ -72,9 +42,61 @@ export interface DefaultChangelogRenderOptions extends ChangelogRenderOptions {
72
42
  */
73
43
  versionTitleDate?: boolean;
74
44
  }
75
- /**
76
- * The default ChangelogRenderer implementation that nx exports for the common case of generating markdown
77
- * from the given commits and other metadata.
78
- */
79
- declare const defaultChangelogRenderer: ChangelogRenderer;
80
- export default defaultChangelogRenderer;
45
+ export default class DefaultChangelogRenderer {
46
+ protected changes: ChangelogChange[];
47
+ protected changelogEntryVersion: string;
48
+ protected project: string | null;
49
+ protected entryWhenNoChanges: string | false;
50
+ protected changelogRenderOptions: DefaultChangelogRenderOptions;
51
+ protected isVersionPlans: boolean;
52
+ protected dependencyBumps?: DependencyBump[];
53
+ protected repoData?: GithubRepoData;
54
+ protected conventionalCommitsConfig: NxReleaseConfig['conventionalCommits'] | null;
55
+ protected relevantChanges: ChangelogChange[];
56
+ protected breakingChanges: string[];
57
+ protected additionalChangesForAuthorsSection: ChangelogChange[];
58
+ /**
59
+ * A ChangelogRenderer class takes in the determined changes and other relevant metadata
60
+ * and returns a string, or a Promise of a string of changelog contents (usually markdown).
61
+ *
62
+ * @param {Object} config The configuration object for the ChangelogRenderer
63
+ * @param {ChangelogChange[]} config.changes The collection of changes to show in the changelog
64
+ * @param {string} config.changelogEntryVersion The version for which we are rendering the current changelog entry
65
+ * @param {string | null} config.project The name of specific project to generate a changelog entry for, or `null` if the overall workspace changelog
66
+ * @param {string | false} config.entryWhenNoChanges The (already interpolated) string to use as the changelog entry when there are no changes, or `false` if no entry should be generated
67
+ * @param {boolean} config.isVersionPlans Whether or not Nx release version plans are the source of truth for the changelog entry
68
+ * @param {ChangelogRenderOptions} config.changelogRenderOptions The options specific to the ChangelogRenderer implementation
69
+ * @param {DependencyBump[]} config.dependencyBumps Optional list of additional dependency bumps that occurred as part of the release, outside of the change data
70
+ * @param {GithubRepoData} config.repoData Resolved data for the current GitHub repository
71
+ * @param {NxReleaseConfig['conventionalCommits'] | null} config.conventionalCommitsConfig The configuration for conventional commits, or null if version plans are being used
72
+ */
73
+ constructor(config: {
74
+ changes: ChangelogChange[];
75
+ changelogEntryVersion: string;
76
+ project: string | null;
77
+ entryWhenNoChanges: string | false;
78
+ isVersionPlans: boolean;
79
+ changelogRenderOptions: DefaultChangelogRenderOptions;
80
+ dependencyBumps?: DependencyBump[];
81
+ repoData?: GithubRepoData;
82
+ conventionalCommitsConfig: NxReleaseConfig['conventionalCommits'] | null;
83
+ });
84
+ protected filterChanges(changes: ChangelogChange[], project: string | null): ChangelogChange[];
85
+ render(): Promise<string>;
86
+ protected preprocessChanges(): void;
87
+ protected shouldRenderEmptyEntry(): boolean;
88
+ protected renderEmptyEntry(): string;
89
+ protected renderVersionTitle(): string;
90
+ protected renderChangesByType(): string[];
91
+ protected hasBreakingChanges(): boolean;
92
+ protected renderBreakingChanges(): string[];
93
+ protected hasDependencyBumps(): boolean;
94
+ protected renderDependencyBumps(): string[];
95
+ protected shouldRenderAuthors(): boolean;
96
+ protected renderAuthors(): Promise<string[]>;
97
+ protected formatChange(change: ChangelogChange): string;
98
+ protected groupChangesByType(): Record<string, ChangelogChange[]>;
99
+ protected groupChangesByScope(changes: ChangelogChange[]): Record<string, ChangelogChange[]>;
100
+ protected extractBreakingChangeExplanation(message: string): string | null;
101
+ protected formatName(name?: string): string;
102
+ }
@@ -6,182 +6,239 @@ const github_1 = require("../../src/command-line/release/utils/github");
6
6
  // axios types and values don't seem to match
7
7
  const _axios = require("axios");
8
8
  const axios = _axios;
9
- /**
10
- * The default ChangelogRenderer implementation that nx exports for the common case of generating markdown
11
- * from the given commits and other metadata.
12
- */
13
- const defaultChangelogRenderer = async ({ projectGraph, changes, releaseVersion, project, entryWhenNoChanges, changelogRenderOptions, dependencyBumps, repoSlug, conventionalCommitsConfig, repoData, }) => {
14
- const markdownLines = [];
15
- // If the current range of changes contains both a commit and its revert, we strip them both from the final list. Changes from version plans are unaffected, as they have no hashes.
16
- for (const change of changes) {
17
- if (change.type === 'revert' && change.revertedHashes) {
18
- for (const revertedHash of change.revertedHashes) {
19
- const revertedCommit = changes.find((c) => c.shortHash && revertedHash.startsWith(c.shortHash));
20
- if (revertedCommit) {
21
- changes.splice(changes.indexOf(revertedCommit), 1);
22
- changes.splice(changes.indexOf(change), 1);
23
- }
24
- }
9
+ class DefaultChangelogRenderer {
10
+ /**
11
+ * A ChangelogRenderer class takes in the determined changes and other relevant metadata
12
+ * and returns a string, or a Promise of a string of changelog contents (usually markdown).
13
+ *
14
+ * @param {Object} config The configuration object for the ChangelogRenderer
15
+ * @param {ChangelogChange[]} config.changes The collection of changes to show in the changelog
16
+ * @param {string} config.changelogEntryVersion The version for which we are rendering the current changelog entry
17
+ * @param {string | null} config.project The name of specific project to generate a changelog entry for, or `null` if the overall workspace changelog
18
+ * @param {string | false} config.entryWhenNoChanges The (already interpolated) string to use as the changelog entry when there are no changes, or `false` if no entry should be generated
19
+ * @param {boolean} config.isVersionPlans Whether or not Nx release version plans are the source of truth for the changelog entry
20
+ * @param {ChangelogRenderOptions} config.changelogRenderOptions The options specific to the ChangelogRenderer implementation
21
+ * @param {DependencyBump[]} config.dependencyBumps Optional list of additional dependency bumps that occurred as part of the release, outside of the change data
22
+ * @param {GithubRepoData} config.repoData Resolved data for the current GitHub repository
23
+ * @param {NxReleaseConfig['conventionalCommits'] | null} config.conventionalCommitsConfig The configuration for conventional commits, or null if version plans are being used
24
+ */
25
+ constructor(config) {
26
+ this.changes = this.filterChanges(config.changes, config.project);
27
+ this.changelogEntryVersion = config.changelogEntryVersion;
28
+ this.project = config.project;
29
+ this.entryWhenNoChanges = config.entryWhenNoChanges;
30
+ this.isVersionPlans = config.isVersionPlans;
31
+ this.changelogRenderOptions = config.changelogRenderOptions;
32
+ this.dependencyBumps = config.dependencyBumps;
33
+ this.repoData = config.repoData;
34
+ this.conventionalCommitsConfig = config.conventionalCommitsConfig;
35
+ this.relevantChanges = [];
36
+ this.breakingChanges = [];
37
+ this.additionalChangesForAuthorsSection = [];
38
+ }
39
+ filterChanges(changes, project) {
40
+ if (project === null) {
41
+ return changes;
25
42
  }
43
+ return changes.filter((c) => c.affectedProjects &&
44
+ (c.affectedProjects === '*' || c.affectedProjects.includes(project)));
26
45
  }
27
- let relevantChanges = changes;
28
- const breakingChanges = [];
29
- // For now to keep the interface of the changelog renderer non-breaking for v19 releases we have a somewhat indirect check for whether or not we are generating a changelog for version plans
30
- const isVersionPlans = !conventionalCommitsConfig;
31
- // Only applicable for version plans
32
- const additionalChangesForAuthorsSection = [];
33
- // Provide a default configuration for version plans to allow most of the subsequent logic to work in the same way it would for conventional commits
34
- // NOTE: The one exception is breaking/major changes, where we do not follow the same structure and instead only show the changes once
35
- if (isVersionPlans) {
36
- conventionalCommitsConfig = {
37
- types: {
38
- feat: conventional_commits_1.DEFAULT_CONVENTIONAL_COMMITS_CONFIG.types.feat,
39
- fix: conventional_commits_1.DEFAULT_CONVENTIONAL_COMMITS_CONFIG.types.fix,
40
- },
41
- };
42
- // Trim down "relevant changes" to only include non-breaking ones so that we can render them differently under version plans,
43
- // but keep track of the changes for the purposes of the authors section
44
- // TODO(v20): Clean this abstraction up as part of the larger overall refactor of changelog rendering
45
- for (let i = 0; i < relevantChanges.length; i++) {
46
- if (relevantChanges[i].isBreaking) {
47
- const change = relevantChanges[i];
48
- additionalChangesForAuthorsSection.push(change);
49
- const line = formatChange(change, changelogRenderOptions, isVersionPlans, repoData);
50
- breakingChanges.push(line);
51
- relevantChanges.splice(i, 1);
52
- }
46
+ async render() {
47
+ const sections = [];
48
+ this.preprocessChanges();
49
+ if (this.shouldRenderEmptyEntry()) {
50
+ return this.renderEmptyEntry();
51
+ }
52
+ sections.push([this.renderVersionTitle()]);
53
+ const changesByType = this.renderChangesByType();
54
+ if (changesByType.length > 0) {
55
+ sections.push(changesByType);
56
+ }
57
+ if (this.hasBreakingChanges()) {
58
+ sections.push(this.renderBreakingChanges());
59
+ }
60
+ if (this.hasDependencyBumps()) {
61
+ sections.push(this.renderDependencyBumps());
53
62
  }
63
+ if (this.shouldRenderAuthors()) {
64
+ sections.push(await this.renderAuthors());
65
+ }
66
+ // Join sections with double newlines, and trim any extra whitespace
67
+ return sections
68
+ .filter((section) => section.length > 0)
69
+ .map((section) => section.join('\n').trim())
70
+ .join('\n\n')
71
+ .trim();
54
72
  }
55
- const changeTypes = conventionalCommitsConfig.types;
56
- // workspace root level changelog
57
- if (project === null) {
58
- // No changes for the workspace
59
- if (relevantChanges.length === 0 && breakingChanges.length === 0) {
60
- if (dependencyBumps?.length) {
61
- applyAdditionalDependencyBumps({
62
- markdownLines,
63
- dependencyBumps,
64
- releaseVersion,
65
- changelogRenderOptions,
66
- });
67
- }
68
- else if (entryWhenNoChanges) {
69
- markdownLines.push('', `${createVersionTitle(releaseVersion, changelogRenderOptions)}\n\n${entryWhenNoChanges}`, '');
73
+ preprocessChanges() {
74
+ this.relevantChanges = [...this.changes];
75
+ this.breakingChanges = [];
76
+ this.additionalChangesForAuthorsSection = [];
77
+ // Filter out reverted changes
78
+ for (let i = this.relevantChanges.length - 1; i >= 0; i--) {
79
+ const change = this.relevantChanges[i];
80
+ if (change.type === 'revert' && change.revertedHashes) {
81
+ for (const revertedHash of change.revertedHashes) {
82
+ const revertedCommitIndex = this.relevantChanges.findIndex((c) => c.shortHash && revertedHash.startsWith(c.shortHash));
83
+ if (revertedCommitIndex !== -1) {
84
+ this.relevantChanges.splice(revertedCommitIndex, 1);
85
+ this.relevantChanges.splice(i, 1);
86
+ i--;
87
+ break;
88
+ }
89
+ }
70
90
  }
71
- return markdownLines.join('\n').trim();
72
91
  }
73
- const typeGroups = groupBy(relevantChanges, 'type');
74
- markdownLines.push('', createVersionTitle(releaseVersion, changelogRenderOptions), '');
75
- for (const type of Object.keys(changeTypes)) {
76
- const group = typeGroups[type];
77
- if (!group || group.length === 0) {
78
- continue;
92
+ if (this.isVersionPlans) {
93
+ this.conventionalCommitsConfig = {
94
+ types: {
95
+ feat: conventional_commits_1.DEFAULT_CONVENTIONAL_COMMITS_CONFIG.types.feat,
96
+ fix: conventional_commits_1.DEFAULT_CONVENTIONAL_COMMITS_CONFIG.types.fix,
97
+ },
98
+ };
99
+ for (let i = this.relevantChanges.length - 1; i >= 0; i--) {
100
+ if (this.relevantChanges[i].isBreaking) {
101
+ const change = this.relevantChanges[i];
102
+ this.additionalChangesForAuthorsSection.push(change);
103
+ const line = this.formatChange(change);
104
+ this.breakingChanges.push(line);
105
+ this.relevantChanges.splice(i, 1);
106
+ }
79
107
  }
80
- markdownLines.push('', '### ' + changeTypes[type].changelog.title, '');
81
- /**
82
- * In order to make the final changelog most readable, we organize changes as follows:
83
- * - By scope, where scopes are in alphabetical order (changes with no scope are listed first)
84
- * - Within a particular scope grouping, we list changes in chronological order
85
- */
86
- const changesInChronologicalOrder = group.reverse();
87
- const changesGroupedByScope = groupBy(changesInChronologicalOrder, 'scope');
88
- const scopesSortedAlphabetically = Object.keys(changesGroupedByScope).sort();
89
- for (const scope of scopesSortedAlphabetically) {
90
- const changes = changesGroupedByScope[scope];
91
- for (const change of changes) {
92
- const line = formatChange(change, changelogRenderOptions, isVersionPlans, repoData);
93
- markdownLines.push(line);
94
- if (change.isBreaking) {
95
- const breakingChangeExplanation = extractBreakingChangeExplanation(change.body);
96
- breakingChanges.push(breakingChangeExplanation
97
- ? `- ${change.scope ? `**${change.scope.trim()}:** ` : ''}${breakingChangeExplanation}`
98
- : line);
99
- }
108
+ }
109
+ else {
110
+ for (const change of this.relevantChanges) {
111
+ if (change.isBreaking) {
112
+ const breakingChangeExplanation = this.extractBreakingChangeExplanation(change.body);
113
+ this.breakingChanges.push(breakingChangeExplanation
114
+ ? `- ${change.scope ? `**${change.scope.trim()}:** ` : ''}${breakingChangeExplanation}`
115
+ : this.formatChange(change));
100
116
  }
101
117
  }
102
118
  }
103
119
  }
104
- else {
105
- // project level changelog
106
- relevantChanges = relevantChanges.filter((c) => c.affectedProjects &&
107
- (c.affectedProjects === '*' || c.affectedProjects.includes(project)));
108
- // Generating for a named project, but that project has no relevant changes in the current set of commits, exit early
109
- if (relevantChanges.length === 0 && breakingChanges.length === 0) {
110
- if (dependencyBumps?.length) {
111
- applyAdditionalDependencyBumps({
112
- markdownLines,
113
- dependencyBumps,
114
- releaseVersion,
115
- changelogRenderOptions,
116
- });
117
- }
118
- else if (entryWhenNoChanges) {
119
- markdownLines.push('', `${createVersionTitle(releaseVersion, changelogRenderOptions)}\n\n${entryWhenNoChanges}`, '');
120
- }
121
- return markdownLines.join('\n').trim();
120
+ shouldRenderEmptyEntry() {
121
+ return (this.relevantChanges.length === 0 &&
122
+ this.breakingChanges.length === 0 &&
123
+ !this.hasDependencyBumps());
124
+ }
125
+ renderEmptyEntry() {
126
+ if (this.hasDependencyBumps()) {
127
+ return [
128
+ this.renderVersionTitle(),
129
+ '',
130
+ ...this.renderDependencyBumps(),
131
+ ].join('\n');
132
+ }
133
+ else if (this.entryWhenNoChanges) {
134
+ return `${this.renderVersionTitle()}\n\n${this.entryWhenNoChanges}`;
122
135
  }
123
- markdownLines.push('', createVersionTitle(releaseVersion, changelogRenderOptions), '');
124
- const typeGroups = groupBy(
125
- // Sort the relevant changes to have the unscoped changes first, before grouping by type
126
- relevantChanges.sort((a, b) => (b.scope ? 1 : 0) - (a.scope ? 1 : 0)), 'type');
136
+ return '';
137
+ }
138
+ renderVersionTitle() {
139
+ const isMajorVersion = `${(0, semver_1.major)(this.changelogEntryVersion)}.0.0` ===
140
+ this.changelogEntryVersion.replace(/^v/, '');
141
+ let maybeDateStr = '';
142
+ if (this.changelogRenderOptions.versionTitleDate) {
143
+ const dateStr = new Date().toISOString().slice(0, 10);
144
+ maybeDateStr = ` (${dateStr})`;
145
+ }
146
+ return isMajorVersion
147
+ ? `# ${this.changelogEntryVersion}${maybeDateStr}`
148
+ : `## ${this.changelogEntryVersion}${maybeDateStr}`;
149
+ }
150
+ renderChangesByType() {
151
+ const markdownLines = [];
152
+ const typeGroups = this.groupChangesByType();
153
+ const changeTypes = this.conventionalCommitsConfig.types;
127
154
  for (const type of Object.keys(changeTypes)) {
128
155
  const group = typeGroups[type];
129
156
  if (!group || group.length === 0) {
130
157
  continue;
131
158
  }
132
159
  markdownLines.push('', `### ${changeTypes[type].changelog.title}`, '');
133
- const changesInChronologicalOrder = group.reverse();
134
- for (const change of changesInChronologicalOrder) {
135
- const line = formatChange(change, changelogRenderOptions, isVersionPlans, repoData);
136
- markdownLines.push(line + '\n');
137
- if (change.isBreaking) {
138
- const breakingChangeExplanation = extractBreakingChangeExplanation(change.body);
139
- breakingChanges.push(breakingChangeExplanation
140
- ? `- ${change.scope ? `**${change.scope.trim()}:** ` : ''}${breakingChangeExplanation}`
141
- : line);
160
+ if (this.project === null) {
161
+ const changesGroupedByScope = this.groupChangesByScope(group);
162
+ const scopesSortedAlphabetically = Object.keys(changesGroupedByScope).sort();
163
+ for (const scope of scopesSortedAlphabetically) {
164
+ const changes = changesGroupedByScope[scope];
165
+ for (const change of changes.reverse()) {
166
+ const line = this.formatChange(change);
167
+ markdownLines.push(line);
168
+ if (change.isBreaking && !this.isVersionPlans) {
169
+ const breakingChangeExplanation = this.extractBreakingChangeExplanation(change.body);
170
+ this.breakingChanges.push(breakingChangeExplanation
171
+ ? `- ${change.scope ? `**${change.scope.trim()}:** ` : ''}${breakingChangeExplanation}`
172
+ : line);
173
+ }
174
+ }
175
+ }
176
+ }
177
+ else {
178
+ // For project-specific changelogs, maintain the original order
179
+ for (const change of group) {
180
+ const line = this.formatChange(change);
181
+ markdownLines.push(line);
182
+ if (change.isBreaking && !this.isVersionPlans) {
183
+ const breakingChangeExplanation = this.extractBreakingChangeExplanation(change.body);
184
+ this.breakingChanges.push(breakingChangeExplanation
185
+ ? `- ${change.scope ? `**${change.scope.trim()}:** ` : ''}${breakingChangeExplanation}`
186
+ : line);
187
+ }
142
188
  }
143
189
  }
144
190
  }
191
+ return markdownLines;
192
+ }
193
+ hasBreakingChanges() {
194
+ return this.breakingChanges.length > 0;
145
195
  }
146
- if (breakingChanges.length > 0) {
147
- markdownLines.push('', '### ⚠️ Breaking Changes', '', ...breakingChanges);
196
+ renderBreakingChanges() {
197
+ const uniqueBreakingChanges = Array.from(new Set(this.breakingChanges));
198
+ return ['### ⚠️ Breaking Changes', '', ...uniqueBreakingChanges];
148
199
  }
149
- if (dependencyBumps?.length) {
150
- applyAdditionalDependencyBumps({
151
- markdownLines,
152
- dependencyBumps,
153
- releaseVersion,
154
- changelogRenderOptions,
200
+ hasDependencyBumps() {
201
+ return this.dependencyBumps && this.dependencyBumps.length > 0;
202
+ }
203
+ renderDependencyBumps() {
204
+ const markdownLines = ['', '### 🧱 Updated Dependencies', ''];
205
+ this.dependencyBumps.forEach(({ dependencyName, newVersion }) => {
206
+ markdownLines.push(`- Updated ${dependencyName} to ${newVersion}`);
155
207
  });
208
+ return markdownLines;
209
+ }
210
+ shouldRenderAuthors() {
211
+ return this.changelogRenderOptions.authors;
156
212
  }
157
- if (changelogRenderOptions.authors) {
213
+ async renderAuthors() {
214
+ const markdownLines = [];
158
215
  const _authors = new Map();
159
216
  for (const change of [
160
- ...relevantChanges,
161
- ...additionalChangesForAuthorsSection,
217
+ ...this.relevantChanges,
218
+ ...this.additionalChangesForAuthorsSection,
162
219
  ]) {
163
- if (!change.author) {
220
+ if (!change.authors) {
164
221
  continue;
165
222
  }
166
- const name = formatName(change.author.name);
167
- if (!name || name.includes('[bot]')) {
168
- continue;
169
- }
170
- if (_authors.has(name)) {
171
- const entry = _authors.get(name);
172
- entry.email.add(change.author.email);
173
- }
174
- else {
175
- _authors.set(name, { email: new Set([change.author.email]) });
223
+ for (const author of change.authors) {
224
+ const name = this.formatName(author.name);
225
+ if (!name || name.includes('[bot]')) {
226
+ continue;
227
+ }
228
+ if (_authors.has(name)) {
229
+ const entry = _authors.get(name);
230
+ entry.email.add(author.email);
231
+ }
232
+ else {
233
+ _authors.set(name, { email: new Set([author.email]) });
234
+ }
176
235
  }
177
236
  }
178
- // Try to map authors to github usernames
179
- if (repoData && changelogRenderOptions.mapAuthorsToGitHubUsernames) {
237
+ if (this.repoData &&
238
+ this.changelogRenderOptions.mapAuthorsToGitHubUsernames) {
180
239
  await Promise.all([..._authors.keys()].map(async (authorName) => {
181
240
  const meta = _authors.get(authorName);
182
241
  for (const email of meta.email) {
183
- // For these pseudo-anonymized emails we can just extract the Github username from before the @
184
- // It could either be in the format: username@ or github_id+username@
185
242
  if (email.endsWith('@users.noreply.github.com')) {
186
243
  const match = email.match(/^(\d+\+)?([^@]+)@users\.noreply\.github\.com$/);
187
244
  if (match && match[2]) {
@@ -189,7 +246,6 @@ const defaultChangelogRenderer = async ({ projectGraph, changes, releaseVersion,
189
246
  break;
190
247
  }
191
248
  }
192
- // Look up any other emails against the ungh.cc API
193
249
  const { data } = await axios
194
250
  .get(`https://ungh.cc/users/find/${email}`)
195
251
  .catch(() => ({ data: { user: null } }));
@@ -206,110 +262,78 @@ const defaultChangelogRenderer = async ({ projectGraph, changes, releaseVersion,
206
262
  }));
207
263
  if (authors.length > 0) {
208
264
  markdownLines.push('', '### ' + '❤️ Thank You', '', ...authors
209
- // Sort the contributors by name
210
265
  .sort((a, b) => a.name.localeCompare(b.name))
211
266
  .map((i) => {
212
- // Tag the author's Github username if we were able to resolve it so that Github adds them as a contributor
213
267
  const github = i.github ? ` @${i.github}` : '';
214
268
  return `- ${i.name}${github}`;
215
269
  }));
216
270
  }
271
+ return markdownLines;
217
272
  }
218
- return markdownLines.join('\n').trim();
219
- };
220
- exports.default = defaultChangelogRenderer;
221
- function applyAdditionalDependencyBumps({ markdownLines, dependencyBumps, releaseVersion, changelogRenderOptions, }) {
222
- if (markdownLines.length === 0) {
223
- markdownLines.push('', `${createVersionTitle(releaseVersion, changelogRenderOptions)}\n`, '');
224
- }
225
- else {
226
- markdownLines.push('');
227
- }
228
- markdownLines.push('### 🧱 Updated Dependencies\n');
229
- dependencyBumps.forEach(({ dependencyName, newVersion }) => {
230
- markdownLines.push(`- Updated ${dependencyName} to ${newVersion}`);
231
- });
232
- markdownLines.push('');
233
- }
234
- function formatName(name = '') {
235
- return name
236
- .split(' ')
237
- .map((p) => p.trim())
238
- .join(' ');
239
- }
240
- function groupBy(items, key) {
241
- const groups = {};
242
- for (const item of items) {
243
- groups[item[key]] = groups[item[key]] || [];
244
- groups[item[key]].push(item);
245
- }
246
- return groups;
247
- }
248
- function formatChange(change, changelogRenderOptions, isVersionPlans, repoData) {
249
- let description = change.description;
250
- let extraLines = [];
251
- let extraLinesStr = '';
252
- if (description.includes('\n')) {
253
- [description, ...extraLines] = description.split('\n');
254
- // Align the extra lines with the start of the description for better readability
255
- const indentation = ' ';
256
- extraLinesStr = extraLines
257
- .filter((l) => l.trim().length > 0)
258
- .map((l) => `${indentation}${l}`)
259
- .join('\n');
260
- }
261
- /**
262
- * In version plans changelogs:
263
- * - don't repeat the breaking change icon
264
- * - don't render the scope
265
- */
266
- let changeLine = '- ' +
267
- (!isVersionPlans && change.isBreaking ? '⚠️ ' : '') +
268
- (!isVersionPlans && change.scope ? `**${change.scope.trim()}:** ` : '') +
269
- description;
270
- if (repoData && changelogRenderOptions.commitReferences) {
271
- changeLine += (0, github_1.formatReferences)(change.githubReferences, repoData);
272
- }
273
- if (extraLinesStr) {
274
- changeLine += '\n\n' + extraLinesStr;
275
- }
276
- return changeLine;
277
- }
278
- /**
279
- * It is common to add further information about a breaking change in the commit body,
280
- * and it is naturally that information that should be included in the BREAKING CHANGES
281
- * section of changelog, rather than repeating the commit title/description.
282
- */
283
- function extractBreakingChangeExplanation(message) {
284
- if (!message) {
285
- return null;
273
+ formatChange(change) {
274
+ let description = change.description;
275
+ let extraLines = [];
276
+ let extraLinesStr = '';
277
+ if (description.includes('\n')) {
278
+ [description, ...extraLines] = description.split('\n');
279
+ const indentation = ' ';
280
+ extraLinesStr = extraLines
281
+ .filter((l) => l.trim().length > 0)
282
+ .map((l) => `${indentation}${l}`)
283
+ .join('\n');
284
+ }
285
+ let changeLine = '- ' +
286
+ (!this.isVersionPlans && change.isBreaking ? '⚠️ ' : '') +
287
+ (!this.isVersionPlans && change.scope
288
+ ? `**${change.scope.trim()}:** `
289
+ : '') +
290
+ description;
291
+ if (this.repoData && this.changelogRenderOptions.commitReferences) {
292
+ changeLine += (0, github_1.formatReferences)(change.githubReferences, this.repoData);
293
+ }
294
+ if (extraLinesStr) {
295
+ changeLine += '\n\n' + extraLinesStr;
296
+ }
297
+ return changeLine;
286
298
  }
287
- const breakingChangeIdentifier = 'BREAKING CHANGE:';
288
- const startIndex = message.indexOf(breakingChangeIdentifier);
289
- if (startIndex === -1) {
290
- // "BREAKING CHANGE:" not found in the message
291
- return null;
299
+ groupChangesByType() {
300
+ const typeGroups = {};
301
+ for (const change of this.relevantChanges) {
302
+ typeGroups[change.type] = typeGroups[change.type] || [];
303
+ typeGroups[change.type].push(change);
304
+ }
305
+ return typeGroups;
292
306
  }
293
- const startOfBreakingChange = startIndex + breakingChangeIdentifier.length;
294
- const endOfBreakingChange = message.indexOf('\n', startOfBreakingChange);
295
- if (endOfBreakingChange === -1) {
296
- // No newline character found, extract till the end of the message
297
- return message.substring(startOfBreakingChange).trim();
307
+ groupChangesByScope(changes) {
308
+ const scopeGroups = {};
309
+ for (const change of changes) {
310
+ const scope = change.scope || '';
311
+ scopeGroups[scope] = scopeGroups[scope] || [];
312
+ scopeGroups[scope].push(change);
313
+ }
314
+ return scopeGroups;
298
315
  }
299
- // Extract and return the breaking change message
300
- return message.substring(startOfBreakingChange, endOfBreakingChange).trim();
301
- }
302
- function createVersionTitle(version, changelogRenderOptions) {
303
- // Normalize by removing any leading `v` during comparison
304
- const isMajorVersion = `${(0, semver_1.major)(version)}.0.0` === version.replace(/^v/, '');
305
- let maybeDateStr = '';
306
- if (changelogRenderOptions.versionTitleDate) {
307
- // YYYY-MM-DD
308
- const dateStr = new Date().toISOString().slice(0, 10);
309
- maybeDateStr = ` (${dateStr})`;
316
+ extractBreakingChangeExplanation(message) {
317
+ if (!message) {
318
+ return null;
319
+ }
320
+ const breakingChangeIdentifier = 'BREAKING CHANGE:';
321
+ const startIndex = message.indexOf(breakingChangeIdentifier);
322
+ if (startIndex === -1) {
323
+ return null;
324
+ }
325
+ const startOfBreakingChange = startIndex + breakingChangeIdentifier.length;
326
+ const endOfBreakingChange = message.indexOf('\n', startOfBreakingChange);
327
+ if (endOfBreakingChange === -1) {
328
+ return message.substring(startOfBreakingChange).trim();
329
+ }
330
+ return message.substring(startOfBreakingChange, endOfBreakingChange).trim();
310
331
  }
311
- if (isMajorVersion) {
312
- return `# ${version}${maybeDateStr}`;
332
+ formatName(name = '') {
333
+ return name
334
+ .split(' ')
335
+ .map((p) => p.trim())
336
+ .join(' ');
313
337
  }
314
- return `## ${version}${maybeDateStr}`;
315
338
  }
339
+ exports.default = DefaultChangelogRenderer;
@@ -103,8 +103,8 @@ async function initializePlugin(pkgName, options, nxJson) {
103
103
  if (options.__overrides_unparsed__.length) {
104
104
  args.push(...options.__overrides_unparsed__);
105
105
  }
106
- await (0, child_process_2.runNxAsync)(`g ${pkgName}:${initGenerator} ${args.join(' ')}`, {
107
- silent: !options.verbose,
106
+ (0, child_process_2.runNxSync)(`g ${pkgName}:${initGenerator} ${args.join(' ')}`, {
107
+ stdio: [0, 1, 2],
108
108
  });
109
109
  }
110
110
  catch (e) {
@@ -29,6 +29,7 @@ const command_object_23 = require("./login/command-object");
29
29
  const command_object_24 = require("./logout/command-object");
30
30
  const command_objects_1 = require("./deprecated/command-objects");
31
31
  const command_object_25 = require("./sync/command-object");
32
+ const output_1 = require("../utils/output");
32
33
  // Ensure that the output takes up the available width of the terminal.
33
34
  yargs.wrap(yargs.terminalWidth());
34
35
  exports.parserConfiguration = {
@@ -82,26 +83,46 @@ exports.commandsObject = yargs
82
83
  .command(command_object_23.yargsLoginCommand)
83
84
  .command(command_object_24.yargsLogoutCommand)
84
85
  .command(resolveConformanceCommandObject())
86
+ .command(resolveConformanceCheckCommandObject())
85
87
  .scriptName('nx')
86
88
  .help()
87
89
  // NOTE: we handle --version in nx.ts, this just tells yargs that the option exists
88
90
  // so that it shows up in help. The default yargs implementation of --version is not
89
91
  // hit, as the implementation in nx.ts is hit first and calls process.exit(0).
90
92
  .version();
93
+ function createMissingConformanceCommand(command) {
94
+ return {
95
+ command,
96
+ // Hide from --help output in the common case of not having the plugin installed
97
+ describe: false,
98
+ handler: () => {
99
+ output_1.output.error({
100
+ title: `${command} is not available`,
101
+ bodyLines: [
102
+ `In order to use the \`nx ${command}\` command you must have an active Powerpack license and the \`@nx/powerpack-conformance\` plugin installed.`,
103
+ '',
104
+ 'To learn more, visit https://nx.dev/features/powerpack/conformance',
105
+ ],
106
+ });
107
+ process.exit(1);
108
+ },
109
+ };
110
+ }
91
111
  function resolveConformanceCommandObject() {
92
112
  try {
93
113
  const { yargsConformanceCommand } = require('@nx/powerpack-conformance');
94
114
  return yargsConformanceCommand;
95
115
  }
96
- catch (e) {
97
- return {
98
- command: 'conformance',
99
- // Hide from --help output in the common case of not having the plugin installed
100
- describe: false,
101
- handler: () => {
102
- // TODO: Add messaging to help with learning more about powerpack and conformance
103
- process.exit(1);
104
- },
105
- };
116
+ catch {
117
+ return createMissingConformanceCommand('conformance');
118
+ }
119
+ }
120
+ function resolveConformanceCheckCommandObject() {
121
+ try {
122
+ const { yargsConformanceCheckCommand, } = require('@nx/powerpack-conformance');
123
+ return yargsConformanceCheckCommand;
124
+ }
125
+ catch {
126
+ return createMissingConformanceCommand('conformance:check');
106
127
  }
107
128
  }
@@ -22,10 +22,10 @@ export interface ChangelogChange {
22
22
  body?: string;
23
23
  isBreaking?: boolean;
24
24
  githubReferences?: Reference[];
25
- author?: {
25
+ authors?: {
26
26
  name: string;
27
27
  email: string;
28
- };
28
+ }[];
29
29
  shortHash?: string;
30
30
  revertedHashes?: string[];
31
31
  }
@@ -162,7 +162,8 @@ function createAPI(overrideReleaseConfig) {
162
162
  body: '',
163
163
  isBreaking: releaseType.isBreaking,
164
164
  githubReferences,
165
- author,
165
+ // TODO(JamesHenry): Implement support for Co-authored-by and adding multiple authors
166
+ authors: [author],
166
167
  affectedProjects: '*',
167
168
  }
168
169
  : vp.triggeredByProjects.map((project) => {
@@ -173,7 +174,8 @@ function createAPI(overrideReleaseConfig) {
173
174
  body: '',
174
175
  isBreaking: releaseType.isBreaking,
175
176
  githubReferences,
176
- author,
177
+ // TODO(JamesHenry): Implement support for Co-authored-by and adding multiple authors
178
+ authors: [author],
177
179
  affectedProjects: [project],
178
180
  };
179
181
  });
@@ -209,7 +211,7 @@ function createAPI(overrideReleaseConfig) {
209
211
  body: c.body,
210
212
  isBreaking: c.isBreaking,
211
213
  githubReferences: c.references,
212
- author: c.author,
214
+ authors: [c.author],
213
215
  shortHash: c.shortHash,
214
216
  revertedHashes: c.revertedHashes,
215
217
  affectedProjects: '*',
@@ -313,13 +315,14 @@ function createAPI(overrideReleaseConfig) {
313
315
  }
314
316
  const releaseType = versionPlanSemverReleaseTypeToChangelogType(bumpForProject);
315
317
  let githubReferences = [];
316
- let author = undefined;
318
+ let authors = [];
317
319
  const parsedCommit = vp.commit
318
320
  ? (0, git_1.parseGitCommit)(vp.commit, true)
319
321
  : null;
320
322
  if (parsedCommit) {
321
323
  githubReferences = parsedCommit.references;
322
- author = parsedCommit.author;
324
+ // TODO(JamesHenry): Implement support for Co-authored-by and adding multiple authors
325
+ authors = [parsedCommit.author];
323
326
  }
324
327
  return {
325
328
  type: releaseType.type,
@@ -329,7 +332,7 @@ function createAPI(overrideReleaseConfig) {
329
332
  isBreaking: releaseType.isBreaking,
330
333
  affectedProjects: Object.keys(vp.projectVersionBumps),
331
334
  githubReferences,
332
- author,
335
+ authors,
333
336
  };
334
337
  })
335
338
  .filter(Boolean);
@@ -365,7 +368,8 @@ function createAPI(overrideReleaseConfig) {
365
368
  body: c.body,
366
369
  isBreaking: c.isBreaking,
367
370
  githubReferences: c.references,
368
- author: c.author,
371
+ // TODO(JamesHenry): Implement support for Co-authored-by and adding multiple authors
372
+ authors: [c.author],
369
373
  shortHash: c.shortHash,
370
374
  revertedHashes: c.revertedHashes,
371
375
  affectedProjects: commitChangesNonProjectFiles(c, fileMap.nonProjectFiles)
@@ -376,15 +380,12 @@ function createAPI(overrideReleaseConfig) {
376
380
  const projectChangelogs = await generateChangelogForProjects({
377
381
  tree,
378
382
  args,
379
- projectGraph,
380
383
  changes,
381
384
  projectsVersionData,
382
385
  releaseGroup,
383
386
  projects: [project],
384
387
  nxReleaseConfig,
385
388
  projectToAdditionalDependencyBumps,
386
- // TODO: remove this after the changelog renderer is refactored to remove coupling with git commits
387
- commits: filterHiddenCommits(commits, nxReleaseConfig.conventionalCommits),
388
389
  });
389
390
  let hasPushed = false;
390
391
  for (const [projectName, projectChangelog] of Object.entries(projectChangelogs)) {
@@ -436,7 +437,8 @@ function createAPI(overrideReleaseConfig) {
436
437
  body: '',
437
438
  isBreaking: releaseType.isBreaking,
438
439
  githubReferences,
439
- author,
440
+ // TODO(JamesHenry): Implement support for Co-authored-by and adding multiple authors
441
+ authors: [author],
440
442
  affectedProjects: '*',
441
443
  }
442
444
  : vp.triggeredByProjects.map((project) => {
@@ -447,7 +449,8 @@ function createAPI(overrideReleaseConfig) {
447
449
  body: '',
448
450
  isBreaking: releaseType.isBreaking,
449
451
  githubReferences,
450
- author,
452
+ // TODO(JamesHenry): Implement support for Co-authored-by and adding multiple authors
453
+ authors: [author],
451
454
  affectedProjects: [project],
452
455
  };
453
456
  });
@@ -482,7 +485,8 @@ function createAPI(overrideReleaseConfig) {
482
485
  body: c.body,
483
486
  isBreaking: c.isBreaking,
484
487
  githubReferences: c.references,
485
- author: c.author,
488
+ // TODO(JamesHenry): Implement support for Co-authored-by and adding multiple authors
489
+ authors: [c.author],
486
490
  shortHash: c.shortHash,
487
491
  revertedHashes: c.revertedHashes,
488
492
  affectedProjects: commitChangesNonProjectFiles(c, fileMap.nonProjectFiles)
@@ -493,15 +497,12 @@ function createAPI(overrideReleaseConfig) {
493
497
  const projectChangelogs = await generateChangelogForProjects({
494
498
  tree,
495
499
  args,
496
- projectGraph,
497
500
  changes,
498
501
  projectsVersionData,
499
502
  releaseGroup,
500
503
  projects: projectNodes,
501
504
  nxReleaseConfig,
502
505
  projectToAdditionalDependencyBumps,
503
- // TODO: remove this after the changelog renderer is refactored to remove coupling with git commits
504
- commits: filterHiddenCommits(commits, nxReleaseConfig.conventionalCommits),
505
506
  });
506
507
  let hasPushed = false;
507
508
  for (const [projectName, projectChangelog] of Object.entries(projectChangelogs)) {
@@ -716,7 +717,7 @@ async function generateChangelogForWorkspace({ tree, args, projectGraph, nxRelea
716
717
  const interactive = args.interactive === 'all' || args.interactive === 'workspace';
717
718
  const dryRun = !!args.dryRun;
718
719
  const gitRemote = args.gitRemote;
719
- const changelogRenderer = (0, resolve_changelog_renderer_1.resolveChangelogRenderer)(config.renderer);
720
+ const ChangelogRendererClass = (0, resolve_changelog_renderer_1.resolveChangelogRenderer)(config.renderer);
720
721
  let interpolatedTreePath = config.file || '';
721
722
  if (interpolatedTreePath) {
722
723
  interpolatedTreePath = (0, utils_1.interpolate)(interpolatedTreePath, {
@@ -736,18 +737,17 @@ async function generateChangelogForWorkspace({ tree, args, projectGraph, nxRelea
736
737
  });
737
738
  }
738
739
  const githubRepoData = (0, github_1.getGitHubRepoData)(gitRemote, config.createRelease);
739
- let contents = await changelogRenderer({
740
- projectGraph,
740
+ const changelogRenderer = new ChangelogRendererClass({
741
741
  changes,
742
- commits,
743
- releaseVersion: releaseVersion.rawVersion,
742
+ changelogEntryVersion: releaseVersion.rawVersion,
744
743
  project: null,
745
- repoSlug: githubRepoData?.slug,
744
+ isVersionPlans: false,
746
745
  repoData: githubRepoData,
747
746
  entryWhenNoChanges: config.entryWhenNoChanges,
748
747
  changelogRenderOptions: config.renderOptions,
749
748
  conventionalCommitsConfig: nxReleaseConfig.conventionalCommits,
750
749
  });
750
+ let contents = await changelogRenderer.render();
751
751
  /**
752
752
  * If interactive mode, make the changelog contents available for the user to modify in their editor of choice,
753
753
  * in a similar style to git interactive rebases/merges.
@@ -789,7 +789,7 @@ async function generateChangelogForWorkspace({ tree, args, projectGraph, nxRelea
789
789
  contents,
790
790
  };
791
791
  }
792
- async function generateChangelogForProjects({ tree, args, projectGraph, changes, commits, projectsVersionData, releaseGroup, projects, nxReleaseConfig, projectToAdditionalDependencyBumps, }) {
792
+ async function generateChangelogForProjects({ tree, args, changes, projectsVersionData, releaseGroup, projects, nxReleaseConfig, projectToAdditionalDependencyBumps, }) {
793
793
  const config = releaseGroup.changelog;
794
794
  // The entire feature is disabled at the release group level, exit early
795
795
  if (config === false) {
@@ -799,7 +799,7 @@ async function generateChangelogForProjects({ tree, args, projectGraph, changes,
799
799
  const interactive = args.interactive === 'all' || args.interactive === 'projects';
800
800
  const dryRun = !!args.dryRun;
801
801
  const gitRemote = args.gitRemote;
802
- const changelogRenderer = (0, resolve_changelog_renderer_1.resolveChangelogRenderer)(config.renderer);
802
+ const ChangelogRendererClass = (0, resolve_changelog_renderer_1.resolveChangelogRenderer)(config.renderer);
803
803
  const projectChangelogs = {};
804
804
  for (const project of projects) {
805
805
  let interpolatedTreePath = config.file || '';
@@ -829,13 +829,10 @@ async function generateChangelogForProjects({ tree, args, projectGraph, changes,
829
829
  });
830
830
  }
831
831
  const githubRepoData = (0, github_1.getGitHubRepoData)(gitRemote, config.createRelease);
832
- let contents = await changelogRenderer({
833
- projectGraph,
832
+ const changelogRenderer = new ChangelogRendererClass({
834
833
  changes,
835
- commits,
836
- releaseVersion: releaseVersion.rawVersion,
834
+ changelogEntryVersion: releaseVersion.rawVersion,
837
835
  project: project.name,
838
- repoSlug: githubRepoData?.slug,
839
836
  repoData: githubRepoData,
840
837
  entryWhenNoChanges: typeof config.entryWhenNoChanges === 'string'
841
838
  ? (0, utils_1.interpolate)(config.entryWhenNoChanges, {
@@ -845,11 +842,13 @@ async function generateChangelogForProjects({ tree, args, projectGraph, changes,
845
842
  })
846
843
  : false,
847
844
  changelogRenderOptions: config.renderOptions,
845
+ isVersionPlans: !!releaseGroup.versionPlans,
848
846
  conventionalCommitsConfig: releaseGroup.versionPlans
849
847
  ? null
850
848
  : nxReleaseConfig.conventionalCommits,
851
849
  dependencyBumps: projectToAdditionalDependencyBumps.get(project.name),
852
850
  });
851
+ let contents = await changelogRenderer.render();
853
852
  /**
854
853
  * If interactive mode, make the changelog contents available for the user to modify in their editor of choice,
855
854
  * in a similar style to git interactive rebases/merges.
@@ -1,2 +1,2 @@
1
- import type { ChangelogRenderer } from '../../../../release/changelog-renderer';
2
- export declare function resolveChangelogRenderer(changelogRendererPath: string): ChangelogRenderer;
1
+ import type ChangelogRenderer from '../../../../release/changelog-renderer';
2
+ export declare function resolveChangelogRenderer(changelogRendererPath: string): typeof ChangelogRenderer;
@@ -10,7 +10,7 @@ function resolveChangelogRenderer(changelogRendererPath) {
10
10
  workspaceRoot: workspace_root_1.workspaceRoot,
11
11
  });
12
12
  // Try and load the provided (or default) changelog renderer
13
- let changelogRenderer;
13
+ let ChangelogRendererClass;
14
14
  let cleanupTranspiler = () => { };
15
15
  try {
16
16
  const rootTsconfigPath = (0, typescript_1.getRootTsConfigPath)();
@@ -18,7 +18,7 @@ function resolveChangelogRenderer(changelogRendererPath) {
18
18
  cleanupTranspiler = (0, register_1.registerTsProject)(rootTsconfigPath);
19
19
  }
20
20
  const r = require(interpolatedChangelogRendererPath);
21
- changelogRenderer = r.default || r;
21
+ ChangelogRendererClass = r.default || r;
22
22
  }
23
23
  catch (err) {
24
24
  throw err;
@@ -26,5 +26,5 @@ function resolveChangelogRenderer(changelogRendererPath) {
26
26
  finally {
27
27
  cleanupTranspiler();
28
28
  }
29
- return changelogRenderer;
29
+ return ChangelogRendererClass;
30
30
  }
@@ -81,9 +81,9 @@ function withRunOptions(yargs) {
81
81
  default: false,
82
82
  })
83
83
  .option('skipSync', {
84
+ describe: 'Skips running the sync generators associated with the tasks.',
84
85
  type: 'boolean',
85
- // TODO(leo): add description and make it visible once it is stable
86
- hidden: true,
86
+ default: false,
87
87
  })
88
88
  .options('cloud', {
89
89
  type: 'boolean',
@@ -353,7 +353,8 @@ export interface NxJsonConfiguration<T = '*' | string[]> {
353
353
  appsDir?: string;
354
354
  };
355
355
  /**
356
- * Available Task Runners
356
+ * @deprecated Custom task runners will no longer be supported in Nx 21. Use Nx Cloud or Nx Powerpack instead.
357
+ * Available Task Runners for Nx to use
357
358
  */
358
359
  tasksRunnerOptions?: {
359
360
  [tasksRunnerName: string]: {
Binary file
@@ -1,5 +1,6 @@
1
1
  import { DefaultTasksRunnerOptions, RemoteCache } from './default-tasks-runner';
2
2
  import { Task } from '../config/task-graph';
3
+ import { NxJsonConfiguration } from '../config/nx-json';
3
4
  export type CachedResult = {
4
5
  terminalOutput: string;
5
6
  outputsPath: string;
@@ -10,6 +11,7 @@ export type TaskWithCachedResult = {
10
11
  task: Task;
11
12
  cachedResult: CachedResult;
12
13
  };
14
+ export declare function dbCacheEnabled(nxJson?: NxJsonConfiguration): boolean;
13
15
  export declare function getCache(options: DefaultTasksRunnerOptions): DbCache | Cache;
14
16
  export declare class DbCache {
15
17
  private readonly options;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Cache = exports.DbCache = void 0;
4
+ exports.dbCacheEnabled = dbCacheEnabled;
4
5
  exports.getCache = getCache;
5
6
  const workspace_root_1 = require("../utils/workspace-root");
6
7
  const path_1 = require("path");
@@ -19,11 +20,14 @@ const update_manager_1 = require("../nx-cloud/update-manager");
19
20
  const get_cloud_options_1 = require("../nx-cloud/utilities/get-cloud-options");
20
21
  const is_ci_1 = require("../utils/is-ci");
21
22
  const output_1 = require("../utils/output");
23
+ function dbCacheEnabled(nxJson = (0, nx_json_1.readNxJson)()) {
24
+ return (process.env.NX_DISABLE_DB !== 'true' &&
25
+ (nxJson.enableDbCache === true || process.env.NX_DB_CACHE === 'true'));
26
+ }
22
27
  // Do not change the order of these arguments as this function is used by nx cloud
23
28
  function getCache(options) {
24
29
  const nxJson = (0, nx_json_1.readNxJson)();
25
- return process.env.NX_DISABLE_DB !== 'true' &&
26
- (nxJson.enableDbCache === true || process.env.NX_DB_CACHE === 'true')
30
+ return dbCacheEnabled(nxJson)
27
31
  ? new DbCache({
28
32
  // Remove this in Nx 21
29
33
  nxCloudRemoteCache: (0, nx_cloud_utils_1.isNxCloudUsed)(nxJson) ? options.remoteCache : null,
@@ -54,10 +54,10 @@ class ProcessTasks {
54
54
  }
55
55
  }
56
56
  this.filterDummyTasks();
57
- for (const projectName of Object.keys(this.dependencies)) {
58
- if (this.dependencies[projectName].length > 1) {
59
- this.dependencies[projectName] = [
60
- ...new Set(this.dependencies[projectName]).values(),
57
+ for (const taskId of Object.keys(this.dependencies)) {
58
+ if (this.dependencies[taskId].length > 0) {
59
+ this.dependencies[taskId] = [
60
+ ...new Set(this.dependencies[taskId].filter((d) => d !== taskId)).values(),
61
61
  ];
62
62
  }
63
63
  }
@@ -511,12 +511,21 @@ function loadTasksRunner(modulePath) {
511
511
  }
512
512
  function getRunner(nxArgs, nxJson) {
513
513
  let runner = nxArgs.runner;
514
- runner = runner || 'default';
514
+ runner = runner ?? 'default';
515
515
  if (runner !== 'default' && !nxJson.tasksRunnerOptions?.[runner]) {
516
516
  throw new Error(`Could not find runner configuration for ${runner}`);
517
517
  }
518
518
  const modulePath = getTasksRunnerPath(runner, nxJson);
519
519
  try {
520
+ if (isCustomRunnerPath(modulePath)) {
521
+ output_1.output.warn({
522
+ title: `Custom task runners will no longer be supported in Nx 21.`,
523
+ bodyLines: [
524
+ `Use Nx Cloud or the Nx Powerpack caches instead.`,
525
+ `For more information, see https://nx.dev/features/powerpack/custom-caching`,
526
+ ],
527
+ });
528
+ }
520
529
  const tasksRunner = loadTasksRunner(modulePath);
521
530
  return {
522
531
  tasksRunner,
@@ -527,6 +536,7 @@ function getRunner(nxArgs, nxJson) {
527
536
  throw new Error(`Could not find runner configuration for ${runner}`);
528
537
  }
529
538
  }
539
+ const defaultTasksRunnerPath = require.resolve('./default-tasks-runner');
530
540
  function getTasksRunnerPath(runner, nxJson) {
531
541
  let modulePath = nxJson.tasksRunnerOptions?.[runner]?.runner;
532
542
  if (modulePath) {
@@ -544,7 +554,7 @@ function getTasksRunnerPath(runner, nxJson) {
544
554
  process.env.NX_CLOUD_ACCESS_TOKEN ||
545
555
  // Nx Cloud ID specified in nxJson
546
556
  nxJson.nxCloudId;
547
- return isCloudRunner ? 'nx-cloud' : require.resolve('./default-tasks-runner');
557
+ return isCloudRunner ? 'nx-cloud' : defaultTasksRunnerPath;
548
558
  }
549
559
  function getRunnerOptions(runner, nxJson, nxArgs, isCloudDefault) {
550
560
  const defaultCacheableOperations = [];
@@ -588,3 +598,6 @@ function getRunnerOptions(runner, nxJson, nxArgs, isCloudDefault) {
588
598
  }
589
599
  return result;
590
600
  }
601
+ function isCustomRunnerPath(modulePath) {
602
+ return !['nx-cloud', '@nrwl/nx-cloud', defaultTasksRunnerPath].includes(modulePath);
603
+ }
@@ -7,6 +7,9 @@ export interface RawNxArgs extends NxArgs {
7
7
  export interface NxArgs {
8
8
  targets?: string[];
9
9
  configuration?: string;
10
+ /**
11
+ * @deprecated Custom task runners will no longer be supported in Nx 21. Use Nx Cloud or Nx Powerpack instead.
12
+ */
10
13
  runner?: string;
11
14
  parallel?: number;
12
15
  untracked?: boolean;