nx 23.0.0-rc.4 → 23.0.1

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/README.md CHANGED
@@ -7,13 +7,12 @@
7
7
 
8
8
  <div style="text-align: center;">
9
9
 
10
- [![CircleCI](https://circleci.com/gh/nrwl/nx.svg?style=svg)](https://circleci.com/gh/nrwl/nx)
11
10
  [![License](https://img.shields.io/npm/l/@nx/workspace.svg?style=flat-square)]()
12
11
  [![NPM Version](https://badge.fury.io/js/nx.svg)](https://www.npmjs.com/package/nx)
13
12
  [![Semantic Release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=flat-square)]()
14
13
  [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
15
- [![Join the chat at https://gitter.im/nrwl-nx/community](https://badges.gitter.im/nrwl-nx/community.svg)](https://gitter.im/nrwl-nx/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
16
14
  [![Join us on the Official Nx Discord Server](https://img.shields.io/discord/1143497901675401286?label=discord)](https://go.nx.dev/community)
15
+ [![Nx Sandboxing](https://staging.nx.app/workspaces/62d013ea0852fe0a2df74438/sandbox-badge.svg)](https://nx.dev/docs/features/ci-features/sandboxing)
17
16
 
18
17
  </div>
19
18
 
@@ -105,5 +105,6 @@ export default class DefaultChangelogRenderer {
105
105
  protected groupChangesByType(): Record<string, ChangelogChange[]>;
106
106
  protected groupChangesByScope(changes: ChangelogChange[]): Record<string, ChangelogChange[]>;
107
107
  protected extractBreakingChangeExplanation(message: string): string | null;
108
+ private isBreakingChangeBoundary;
108
109
  protected formatName(name?: string): string;
109
110
  }
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const semver_1 = require("semver");
4
+ // Stripped from breaking change explanations (e.g. bot/session markers).
5
+ const HtmlCommentRegex = /<!--[\s\S]*?-->/g;
4
6
  class DefaultChangelogRenderer {
5
7
  /**
6
8
  * A ChangelogRenderer class takes in the determined changes and other relevant metadata
@@ -342,20 +344,33 @@ class DefaultChangelogRenderer {
342
344
  return null;
343
345
  }
344
346
  const startOfBreakingChange = startIndex + breakingChangeIdentifier.length;
345
- // Extract all text after BREAKING CHANGE: until we hit a Co-authored-by section or git metadata
346
- let endOfBreakingChange = message.length;
347
- const coAuthoredBySection = message.indexOf('---------\n\nCo-authored-by:');
348
- if (coAuthoredBySection !== -1) {
349
- endOfBreakingChange = coAuthoredBySection;
350
- }
351
- else {
352
- // Look for the git metadata delimiter (a line with just ")
353
- const gitMetadataMarker = message.indexOf('"\n', startOfBreakingChange);
354
- if (gitMetadataMarker !== -1) {
355
- endOfBreakingChange = gitMetadataMarker;
347
+ // A squash-merged PR body can trail the note with template sections,
348
+ // trailers, and git metadata. Strip comments (they can appear anywhere),
349
+ // then keep lines until the first boundary.
350
+ const lines = message
351
+ .slice(startOfBreakingChange)
352
+ .replace(HtmlCommentRegex, '')
353
+ .split('\n');
354
+ // The rest of the "BREAKING CHANGE:" line is always kept; scan from the next.
355
+ const explanationLines = [lines[0]];
356
+ for (let i = 1; i < lines.length; i++) {
357
+ if (this.isBreakingChangeBoundary(lines[i])) {
358
+ break;
356
359
  }
360
+ explanationLines.push(lines[i]);
357
361
  }
358
- return message.substring(startOfBreakingChange, endOfBreakingChange).trim();
362
+ return explanationLines.join('\n').trim();
363
+ }
364
+ isBreakingChangeBoundary(line) {
365
+ const trimmed = line.trim();
366
+ return (
367
+ // Markdown heading (e.g. "## Related issues")
368
+ /^#{1,6}\s/.test(trimmed) ||
369
+ // Horizontal rule / separator
370
+ /^-{3,}$/.test(trimmed) ||
371
+ /^Co-authored-by:/i.test(trimmed) ||
372
+ // Git metadata delimiter following the body
373
+ trimmed === '"');
359
374
  }
360
375
  formatName(name = '') {
361
376
  return name
@@ -80,9 +80,10 @@ function buildScopeRules(mode) {
80
80
  return [
81
81
  `<scope_rules>`,
82
82
  `- Apply only the changes the migration prompt asks for.`,
83
- `- Do not refactor, reformat, or update dependencies beyond what the migration prompt directs.`,
83
+ `- Do not refactor or update dependencies beyond what the migration prompt directs, and do not reformat files you did not change.`,
84
+ `- After applying your changes and before writing the handoff, format the files you created or modified so they match the workspace's style. If the workspace uses Prettier, run \`nx format:write\`, which formats your uncommitted changes; if it has no formatter configured, skip this.`,
84
85
  `- Do not modify files outside the workspace root.`,
85
- `- Do not run other \`nx\` commands that mutate workspace state (\`nx migrate\`, \`nx reset\`, \`nx run-many\`, generators, etc.). Read-only inspection (\`nx show\`, \`nx graph --file\`, reading files) is fine.`,
86
+ `- Do not run other \`nx\` commands that mutate workspace state (\`nx migrate\`, \`nx reset\`, \`nx run-many\`, generators, etc.), except \`nx format:write\` to format the files you changed. Read-only inspection (\`nx show\`, \`nx graph --file\`, reading files) is fine.`,
86
87
  `- If the migration instructions are unclear, internally inconsistent, or conflict with the current workspace state, ask the user for direction (see the handoff contract). Do not guess.`,
87
88
  `</scope_rules>`,
88
89
  ].join('\n');
@@ -1413,7 +1413,8 @@ async function generateMigrationsJsonAndUpdatePackageJson(root, opts, fetch) {
1413
1413
  // the user already has. Drop those before writing; nx migrate is
1414
1414
  // forward-only, never a downgrade.
1415
1415
  phase = 'package_updates';
1416
- const writableUpdates = (0, update_filters_1.filterDowngradedUpdates)(packageUpdates, originalPackageJson, installedPackageVersions);
1416
+ // Resolve catalog: specifiers first so the filter compares real versions.
1417
+ const writableUpdates = (0, update_filters_1.filterDowngradedUpdates)(packageUpdates, (0, catalog_1.resolveCatalogSpecifiers)(originalPackageJson), installedPackageVersions);
1417
1418
  const wrotePackageJson = await updatePackageJson(root, writableUpdates);
1418
1419
  const wroteNxJsonInstallation = await updateInstallationDetails(root, writableUpdates);
1419
1420
  const promptMigrationFiles = (0, prompt_files_1.writePromptMigrationFiles)(root, migrations, promptContents ?? {}, packageUpdates[walkedTargetPackage].version);
@@ -137,9 +137,9 @@ async function resolveWithPolicy(packageName, version, policy, applySideEffects)
137
137
  if (applySideEffects && outcome.version !== outcome.unconstrained) {
138
138
  reportChangedOutcome(packageName, spec, outcome.version, outcome.unconstrained, policy);
139
139
  }
140
- if (outcome.immature && applySideEffects) {
141
- handleImmaturePick(packageName, outcome.version, policy);
142
- }
140
+ // An immature loose pick is returned as-is. nx does not write the
141
+ // minimumReleaseAgeExclude entry here: migrate does not replace the install,
142
+ // so the real `pnpm install` (>=11.1.3) auto-writes it itself.
143
143
  return outcome.version;
144
144
  }
145
145
  catch (e) {
@@ -171,28 +171,6 @@ function reportChangedOutcome(packageName, spec, picked, unconstrained, policy)
171
171
  title: `Resolved ${packageName}@${picked} instead of ${unconstrained}: ${policy.sourceDescription}.`,
172
172
  });
173
173
  }
174
- // pnpm v11 loose installs an immature version; >=11.1.3 also records it as an
175
- // exclude in pnpm-workspace.yaml. Mirror that so a later real install agrees.
176
- function handleImmaturePick(packageName, version, policy) {
177
- if (policy.behavior.packageManager !== 'pnpm' ||
178
- !policy.behavior.writesExcludes) {
179
- return;
180
- }
181
- const added = (0, pnpm_exclude_writer_1.appendMinimumReleaseAgeExcludes)(workspace_root_1.workspaceRoot, [
182
- `${packageName}@${version}`,
183
- ]);
184
- // Already present (e.g. a prior pick or a pre-existing entry): nothing written,
185
- // so do not claim we added it.
186
- if (added.length === 0) {
187
- return;
188
- }
189
- output_1.output.log({
190
- title: `Added ${packageName}@${version} to minimumReleaseAgeExclude in pnpm-workspace.yaml.`,
191
- bodyLines: [
192
- `It is within the ${policy.sourceDescription} window; this mirrors what pnpm would write at install time.`,
193
- ],
194
- });
195
- }
196
174
  async function handleViolation(packageName, error, policy) {
197
175
  const isPnpmStrict = policy.behavior.packageManager === 'pnpm' &&
198
176
  policy.behavior.strict &&
@@ -35,7 +35,8 @@ function filterDowngradedUpdates(packageUpdates, packageJson, getInstalledVersio
35
35
  if (!specifier) {
36
36
  continue;
37
37
  }
38
- const floor = (0, semver_1.minVersion)(specifier);
38
+ // Skip specifiers that aren't semver ranges (workspace:/npm:/git/file).
39
+ const floor = (0, semver_1.validRange)(specifier) ? (0, semver_1.minVersion)(specifier) : null;
39
40
  if (floor && (0, semver_1.lt)(floor.version, resolvedNorm)) {
40
41
  result[name] = update;
41
42
  }
@@ -181,7 +181,7 @@ function sleep(ms) {
181
181
  return new Promise((resolve) => setTimeout(resolve, ms));
182
182
  }
183
183
  async function connectExistingRepoToNxCloudPrompt(command = 'init', key = 'setupNxCloud') {
184
- const res = await nxCloudPrompt(key);
184
+ const res = await nxCloudPrompt(key, utmMediumForCommand(command));
185
185
  await (0, ab_testing_1.recordStat)({
186
186
  command,
187
187
  nxVersion: versions_1.nxVersion,
@@ -200,7 +200,7 @@ async function connectExistingRepoToNxCloudPrompt(command = 'init', key = 'setup
200
200
  return res;
201
201
  }
202
202
  async function connectToNxCloudWithPrompt(command) {
203
- const setNxCloud = await nxCloudPrompt('setupNxCloud');
203
+ const setNxCloud = await nxCloudPrompt('setupNxCloud', utmMediumForCommand(command));
204
204
  let useCloud = false;
205
205
  if (setNxCloud === 'yes') {
206
206
  useCloud = await connectToNxCloudCommand({ generateToken: false }, command);
@@ -229,7 +229,17 @@ async function connectToNxCloudWithPrompt(command) {
229
229
  },
230
230
  });
231
231
  }
232
- async function nxCloudPrompt(key) {
232
+ function utmMediumForCommand(command) {
233
+ switch (command) {
234
+ case 'migrate':
235
+ return 'nx-migrate';
236
+ case 'view-logs':
237
+ return 'nx-connect';
238
+ default:
239
+ return 'nx-init';
240
+ }
241
+ }
242
+ async function nxCloudPrompt(key, utmMedium) {
233
243
  const { message, choices, initial, footer, hint } = ab_testing_1.messages.getPrompt(key);
234
244
  const promptConfig = {
235
245
  name: 'NxCloud',
@@ -239,7 +249,7 @@ async function nxCloudPrompt(key) {
239
249
  initial,
240
250
  }; // meeroslav: types in enquirer are not up to date
241
251
  if (footer) {
242
- promptConfig.footer = () => pc.dim(footer);
252
+ promptConfig.footer = () => pc.dim(`${footer} ${(0, ab_testing_1.nxCloudHyperlink)(utmMedium)}`);
243
253
  }
244
254
  if (hint) {
245
255
  promptConfig.hint = () => pc.dim(hint);