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

Sign up to get free protection for your applications and to get access to all the features.
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;