nx 20.0.0-canary.20241001-8fa7065 → 20.0.0-canary.20241003-84a5c7a

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. package/.eslintrc.json +12 -2
  2. package/migrations.json +0 -37
  3. package/package.json +11 -11
  4. package/release/changelog-renderer/index.d.ts +60 -38
  5. package/release/changelog-renderer/index.js +260 -236
  6. package/src/command-line/add/add.js +2 -2
  7. package/src/command-line/nx-commands.js +31 -10
  8. package/src/command-line/release/changelog.d.ts +2 -2
  9. package/src/command-line/release/changelog.js +28 -29
  10. package/src/command-line/release/index.d.ts +5 -2
  11. package/src/command-line/release/publish.d.ts +6 -1
  12. package/src/command-line/release/publish.js +31 -25
  13. package/src/command-line/release/utils/print-changes.js +6 -4
  14. package/src/command-line/release/utils/resolve-changelog-renderer.d.ts +2 -2
  15. package/src/command-line/release/utils/resolve-changelog-renderer.js +3 -3
  16. package/src/command-line/release/utils/resolve-nx-json-error-message.js +4 -3
  17. package/src/command-line/release/version.d.ts +3 -3
  18. package/src/command-line/yargs-utils/shared-options.js +2 -2
  19. package/src/config/nx-json.d.ts +2 -1
  20. package/src/config/workspace-json-project-json.d.ts +14 -0
  21. package/src/core/graph/main.js +1 -1
  22. package/src/native/nx.wasm32-wasi.wasm +0 -0
  23. package/src/nx-cloud/utilities/axios.js +1 -2
  24. package/src/nx-cloud/utilities/onboarding.js +2 -2
  25. package/src/nx-cloud/utilities/url-shorten.js +5 -5
  26. package/src/project-graph/file-utils.d.ts +2 -2
  27. package/src/project-graph/file-utils.js +2 -2
  28. package/src/tasks-runner/cache.d.ts +2 -1
  29. package/src/tasks-runner/cache.js +10 -6
  30. package/src/tasks-runner/create-task-graph.d.ts +2 -0
  31. package/src/tasks-runner/create-task-graph.js +39 -5
  32. package/src/tasks-runner/run-command.js +15 -2
  33. package/src/tasks-runner/task-orchestrator.js +1 -1
  34. package/src/utils/command-line-utils.d.ts +3 -0
  35. package/src/utils/git-utils.js +2 -2
  36. package/src/migrations/update-15-0-0/prefix-outputs.d.ts +0 -2
  37. package/src/migrations/update-15-0-0/prefix-outputs.js +0 -49
  38. package/src/migrations/update-16-0-0/remove-nrwl-cli.d.ts +0 -2
  39. package/src/migrations/update-16-0-0/remove-nrwl-cli.js +0 -16
  40. package/src/migrations/update-16-0-0/update-depends-on-to-tokens.d.ts +0 -2
  41. package/src/migrations/update-16-0-0/update-depends-on-to-tokens.js +0 -97
  42. package/src/migrations/update-16-0-0/update-nx-cloud-runner.d.ts +0 -2
  43. package/src/migrations/update-16-0-0/update-nx-cloud-runner.js +0 -29
  44. package/src/migrations/update-16-2-0/remove-run-commands-output-path.d.ts +0 -2
  45. package/src/migrations/update-16-2-0/remove-run-commands-output-path.js +0 -45
  46. package/src/migrations/update-16-8-0/escape-dollar-sign-env-variables.d.ts +0 -12
  47. package/src/migrations/update-16-8-0/escape-dollar-sign-env-variables.js +0 -67
@@ -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
  }