@mui/internal-code-infra 0.0.4-canary.10 → 0.0.4-canary.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +21 -7
  3. package/build/babel-config.d.mts +5 -1
  4. package/build/brokenLinksChecker/crawlWorker.d.mts +1 -0
  5. package/build/brokenLinksChecker/index.d.mts +192 -2
  6. package/build/changelog/fetchChangelogs.d.mts +3 -0
  7. package/build/changelog/types.d.ts +3 -1
  8. package/build/cli/cmdArgosPush.d.mts +8 -2
  9. package/build/cli/cmdBuild.d.mts +48 -2
  10. package/build/cli/cmdCopyFiles.d.mts +14 -2
  11. package/build/cli/cmdExtractErrorCodes.d.mts +2 -2
  12. package/build/cli/cmdGenerateChangelog.d.mts +17 -2
  13. package/build/cli/cmdGithubAuth.d.mts +2 -2
  14. package/build/cli/cmdListWorkspaces.d.mts +14 -2
  15. package/build/cli/cmdNetlifyIgnore.d.mts +12 -2
  16. package/build/cli/cmdPublish.d.mts +23 -4
  17. package/build/cli/cmdPublishCanary.d.mts +20 -2
  18. package/build/cli/cmdPublishNewPackage.d.mts +10 -2
  19. package/build/cli/cmdRenameScope.d.mts +18 -0
  20. package/build/cli/cmdSetVersionOverrides.d.mts +5 -2
  21. package/build/cli/cmdVale.d.mts +46 -0
  22. package/build/cli/cmdValidateBuiltTypes.d.mts +2 -2
  23. package/build/eslint/baseConfig.d.mts +3 -1
  24. package/build/eslint/mui/rules/disallow-react-api-in-server-components.d.mts +2 -2
  25. package/build/eslint/mui/rules/docgen-ignore-before-comment.d.mts +2 -2
  26. package/build/eslint/mui/rules/no-floating-cleanup.d.mts +5 -0
  27. package/build/eslint/mui/rules/no-guarded-throw.d.mts +31 -0
  28. package/build/eslint/mui/rules/no-presentation-role.d.mts +5 -0
  29. package/build/eslint/mui/rules/no-restricted-resolved-imports.d.mts +8 -2
  30. package/build/eslint/mui/rules/nodeEnvUtils.d.mts +18 -0
  31. package/build/remark/config.d.mts +43 -0
  32. package/build/remark/createLintTester.d.mts +10 -0
  33. package/build/remark/firstBlockHeading.d.mts +4 -0
  34. package/build/remark/gitDiff.d.mts +2 -0
  35. package/build/remark/noSpaceInLinks.d.mts +2 -0
  36. package/build/remark/straightQuotes.d.mts +2 -0
  37. package/build/remark/tableAlignment.d.mts +2 -0
  38. package/build/remark/terminalLanguage.d.mts +2 -0
  39. package/build/utils/babel.d.mts +7 -1
  40. package/build/utils/build.d.mts +59 -37
  41. package/build/utils/extractErrorCodes.d.mts +9 -0
  42. package/build/utils/git.d.mts +16 -0
  43. package/build/utils/github.d.mts +13 -1
  44. package/build/utils/pnpm.d.mts +176 -17
  45. package/build/utils/testUtils.d.mts +28 -0
  46. package/build/utils/typescript.d.mts +6 -16
  47. package/package.json +77 -53
  48. package/src/babel-config.mjs +3 -1
  49. package/src/brokenLinksChecker/crawlWorker.mjs +240 -0
  50. package/src/brokenLinksChecker/index.mjs +263 -188
  51. package/src/changelog/fetchChangelogs.mjs +8 -2
  52. package/src/changelog/renderChangelog.mjs +1 -1
  53. package/src/changelog/types.ts +3 -1
  54. package/src/cli/cmdBuild.mjs +51 -85
  55. package/src/cli/cmdListWorkspaces.mjs +7 -1
  56. package/src/cli/cmdNetlifyIgnore.mjs +30 -5
  57. package/src/cli/cmdPublish.mjs +79 -37
  58. package/src/cli/cmdPublishCanary.mjs +96 -24
  59. package/src/cli/cmdPublishNewPackage.mjs +31 -8
  60. package/src/cli/cmdRenameScope.mjs +76 -0
  61. package/src/cli/cmdSetVersionOverrides.mjs +8 -9
  62. package/src/cli/cmdVale.mjs +513 -0
  63. package/src/cli/index.mjs +4 -0
  64. package/src/cli/packageJson.d.ts +2 -3
  65. package/src/eslint/baseConfig.mjs +51 -19
  66. package/src/eslint/docsConfig.mjs +2 -1
  67. package/src/eslint/mui/config.mjs +24 -4
  68. package/src/eslint/mui/index.mjs +6 -0
  69. package/src/eslint/mui/rules/no-floating-cleanup.mjs +187 -0
  70. package/src/eslint/mui/rules/no-guarded-throw.mjs +115 -0
  71. package/src/eslint/mui/rules/no-presentation-role.mjs +60 -0
  72. package/src/eslint/mui/rules/nodeEnvUtils.mjs +52 -0
  73. package/src/eslint/mui/rules/require-dev-wrapper.mjs +25 -40
  74. package/src/estree-typescript.d.ts +1 -1
  75. package/src/remark/config.mjs +157 -0
  76. package/src/remark/createLintTester.mjs +19 -0
  77. package/src/remark/firstBlockHeading.mjs +87 -0
  78. package/src/remark/gitDiff.mjs +43 -0
  79. package/src/remark/noSpaceInLinks.mjs +42 -0
  80. package/src/remark/straightQuotes.mjs +31 -0
  81. package/src/remark/tableAlignment.mjs +23 -0
  82. package/src/remark/terminalLanguage.mjs +19 -0
  83. package/src/stylelint/index.mjs +2 -2
  84. package/src/untyped-plugins.d.ts +14 -14
  85. package/src/utils/build.mjs +604 -270
  86. package/src/utils/git.mjs +27 -7
  87. package/src/utils/pnpm.mjs +444 -53
  88. package/src/utils/testUtils.mjs +53 -0
  89. package/src/utils/typescript.mjs +23 -42
  90. package/vale/.vale.ini +1 -0
  91. package/vale/styles/MUI/CorrectReferenceAllCases.yml +42 -0
  92. package/vale/styles/MUI/CorrectRererenceCased.yml +14 -0
  93. package/vale/styles/MUI/GoogleLatin.yml +11 -0
  94. package/vale/styles/MUI/MuiBrandName.yml +22 -0
  95. package/vale/styles/MUI/NoBritish.yml +112 -0
  96. package/vale/styles/MUI/NoCompanyName.yml +17 -0
  97. package/build/markdownlint/duplicate-h1.d.mts +0 -56
  98. package/build/markdownlint/git-diff.d.mts +0 -11
  99. package/build/markdownlint/index.d.mts +0 -52
  100. package/build/markdownlint/straight-quotes.d.mts +0 -11
  101. package/build/markdownlint/table-alignment.d.mts +0 -11
  102. package/build/markdownlint/terminal-language.d.mts +0 -11
  103. package/src/brokenLinksChecker/__fixtures__/static-site/broken-links.html +0 -20
  104. package/src/brokenLinksChecker/__fixtures__/static-site/broken-targets.html +0 -22
  105. package/src/brokenLinksChecker/__fixtures__/static-site/example.md +0 -20
  106. package/src/brokenLinksChecker/__fixtures__/static-site/external-links.html +0 -21
  107. package/src/brokenLinksChecker/__fixtures__/static-site/ignored-page.html +0 -17
  108. package/src/brokenLinksChecker/__fixtures__/static-site/index.html +0 -28
  109. package/src/brokenLinksChecker/__fixtures__/static-site/known-targets.json +0 -5
  110. package/src/brokenLinksChecker/__fixtures__/static-site/nested/page.html +0 -21
  111. package/src/brokenLinksChecker/__fixtures__/static-site/orphaned-page.html +0 -20
  112. package/src/brokenLinksChecker/__fixtures__/static-site/page-with-api-links.html +0 -20
  113. package/src/brokenLinksChecker/__fixtures__/static-site/page-with-custom-targets.html +0 -24
  114. package/src/brokenLinksChecker/__fixtures__/static-site/page-with-ignored-content.html +0 -28
  115. package/src/brokenLinksChecker/__fixtures__/static-site/page-with-known-target-links.html +0 -19
  116. package/src/brokenLinksChecker/__fixtures__/static-site/unclosed-tags.html +0 -1
  117. package/src/brokenLinksChecker/__fixtures__/static-site/valid.html +0 -20
  118. package/src/brokenLinksChecker/__fixtures__/static-site/with-anchors.html +0 -31
  119. package/src/brokenLinksChecker/index.test.ts +0 -261
  120. package/src/changelog/categorizeCommits.test.ts +0 -319
  121. package/src/changelog/filterCommits.test.ts +0 -363
  122. package/src/changelog/parseCommitLabels.test.ts +0 -509
  123. package/src/changelog/renderChangelog.test.ts +0 -378
  124. package/src/changelog/sortSections.test.ts +0 -199
  125. package/src/eslint/mui/rules/add-undef-to-optional.test.mjs +0 -361
  126. package/src/eslint/mui/rules/consistent-production-guard.test.mjs +0 -162
  127. package/src/eslint/mui/rules/disallow-active-elements-as-key-event-target.test.mjs +0 -66
  128. package/src/eslint/mui/rules/disallow-react-api-in-server-components.test.mjs +0 -305
  129. package/src/eslint/mui/rules/docgen-ignore-before-comment.test.mjs +0 -52
  130. package/src/eslint/mui/rules/flatten-parentheses.test.mjs +0 -245
  131. package/src/eslint/mui/rules/mui-name-matches-component-name.test.mjs +0 -247
  132. package/src/eslint/mui/rules/no-empty-box.test.mjs +0 -40
  133. package/src/eslint/mui/rules/no-styled-box.test.mjs +0 -73
  134. package/src/eslint/mui/rules/require-dev-wrapper.test.mjs +0 -265
  135. package/src/eslint/mui/rules/rules-of-use-theme-variants.test.mjs +0 -149
  136. package/src/eslint/mui/rules/straight-quotes.test.mjs +0 -67
  137. package/src/markdownlint/duplicate-h1.mjs +0 -69
  138. package/src/markdownlint/git-diff.mjs +0 -31
  139. package/src/markdownlint/index.mjs +0 -62
  140. package/src/markdownlint/straight-quotes.mjs +0 -26
  141. package/src/markdownlint/table-alignment.mjs +0 -42
  142. package/src/markdownlint/terminal-language.mjs +0 -19
  143. package/src/utils/build.test.mjs +0 -705
  144. package/src/utils/template.test.mjs +0 -133
  145. package/src/utils/typescript.test.mjs +0 -380
@@ -93,7 +93,11 @@ async function fetchCommitsForPackage({ packagePath }) {
93
93
  return results;
94
94
  }
95
95
 
96
- const AUTHOR_EXCLUDE_LIST = ['renovate[bot]', 'dependabot[bot]'];
96
+ const AUTHOR_EXCLUDE_LIST = new Set([
97
+ 'renovate[bot]',
98
+ 'dependabot[bot]',
99
+ 'code-infra-renovate[bot]',
100
+ ]);
97
101
 
98
102
  /**
99
103
  * @param {string} message
@@ -144,7 +148,7 @@ async function prepareChangelogsFromGitCli(packagesToPublish, allPackages, canar
144
148
  // Exclude commits authored by bots
145
149
  .filter(
146
150
  // We want to allow commits from copilot or other AI tools, so only filter known bots
147
- (commit) => !AUTHOR_EXCLUDE_LIST.includes(commit.author),
151
+ (commit) => !AUTHOR_EXCLUDE_LIST.has(commit.author),
148
152
  )
149
153
  .map((commit) => `- ${cleanupCommitMessage(commit.message)} by ${commit.author}`);
150
154
 
@@ -159,7 +163,10 @@ async function prepareChangelogsFromGitCli(packagesToPublish, allPackages, canar
159
163
 
160
164
  const transitiveDepSets = await Promise.all(
161
165
  allPackages.map((pkg) =>
162
- getTransitiveDependencies([pkg.name], { includeDev: false, workspacePathByName }),
166
+ getTransitiveDependencies([pkg.name], {
167
+ includeDev: false,
168
+ workspacePathByName,
169
+ }),
163
170
  ),
164
171
  );
165
172
 
@@ -225,6 +232,41 @@ async function prepareChangelogsForPackages(packagesToPublish, allPackages, cana
225
232
  return changelogs;
226
233
  }
227
234
 
235
+ /**
236
+ * Create or replace a GitHub release. Attempts to create the release first,
237
+ * and if it already exists (422), deletes the existing one and retries.
238
+ *
239
+ * @param {InstanceType<typeof Octokit>} octokit
240
+ * @param {NonNullable<Parameters<Octokit['repos']['createRelease']>[0]>} params
241
+ */
242
+ async function upsertGitHubRelease(octokit, params) {
243
+ try {
244
+ return await octokit.repos.createRelease(params);
245
+ } catch (/** @type {any} */ error) {
246
+ const isAlreadyExists =
247
+ error.status === 422 &&
248
+ error.response?.data?.errors?.some(
249
+ (/** @type {any} */ err) => err.code === 'already_exists' && err.field === 'tag_name',
250
+ );
251
+ if (!isAlreadyExists) {
252
+ throw error;
253
+ }
254
+ }
255
+
256
+ // Release already exists — delete and recreate
257
+ const existing = await octokit.repos.getReleaseByTag({
258
+ owner: params.owner,
259
+ repo: params.repo,
260
+ tag: params.tag_name,
261
+ });
262
+ await octokit.repos.deleteRelease({
263
+ owner: params.owner,
264
+ repo: params.repo,
265
+ release_id: existing.data.id,
266
+ });
267
+ return octokit.repos.createRelease(params);
268
+ }
269
+
228
270
  /**
229
271
  * Create GitHub releases and tags for published packages
230
272
  * @param {PublicPackage[]} publishedPackages - Packages that were published
@@ -277,15 +319,20 @@ async function createGitHubReleasesForPackages(
277
319
  GIT_COMMITTER_NAME: 'Code infra',
278
320
  GIT_COMMITTER_EMAIL: 'code-infra@mui.com',
279
321
  },
280
- })`git tag -a ${tagName} -m ${`Canary release ${pkg.name}@${version}`}`;
322
+ })`git tag -fa ${tagName} -m ${`Canary release ${pkg.name}@${version}`}`;
281
323
 
324
+ // Force-push to handle retries where the tag already exists from a previous
325
+ // failed publish. The npm registry is the source of truth for published
326
+ // versions, so it's safe to rewrite a tag even if it points to a different
327
+ // commit — it just means the prior publish for this version failed partway
328
+ // through and the GitHub release needs to be recreated.
282
329
  // eslint-disable-next-line no-await-in-loop
283
- await $`git push origin ${tagName}`;
330
+ await $`git push --force origin ${tagName}`;
284
331
  console.log(`āœ… Created and pushed git tag: ${tagName}`);
285
332
 
286
333
  // Create GitHub release
287
334
  // eslint-disable-next-line no-await-in-loop
288
- const res = await octokit.repos.createRelease({
335
+ const res = await upsertGitHubRelease(octokit, {
289
336
  owner: repoInfo.owner,
290
337
  repo: repoInfo.repo,
291
338
  tag_name: tagName,
@@ -315,7 +362,14 @@ async function getLastCanaryTag() {
315
362
  // Tag might not exist locally, which is fine
316
363
  }
317
364
 
318
- await $`git fetch origin tag ${CANARY_TAG}`;
365
+ try {
366
+ await $`git fetch origin tag ${CANARY_TAG}`;
367
+ } catch (err) {
368
+ // Tag might not exist on the remote yet (first canary run), which is fine
369
+ if (!(/** @type {Error} */ (err).message?.includes("couldn't find remote ref"))) {
370
+ throw err;
371
+ }
372
+ }
319
373
  const { stdout: remoteCanaryTag } = await $`git ls-remote --tags origin ${CANARY_TAG}`;
320
374
  return remoteCanaryTag.trim() ? CANARY_TAG : null;
321
375
  }
@@ -427,20 +481,23 @@ async function publishCanaryVersions(
427
481
  }
428
482
 
429
483
  // Third pass: publish only the changed packages using recursive publish
430
- let publishSuccess = false;
484
+ /** @type {Set<string>} */
485
+ const publishedNames = new Set();
431
486
  try {
432
487
  console.log(`šŸ“¤ Publishing ${packagesToPublish.length} canary versions...`);
433
- await publishPackages(packagesToPublish, {
488
+ const publishedPackages = await publishPackages(packagesToPublish, {
434
489
  dryRun: options.dryRun,
435
490
  noGitChecks: true,
436
491
  tag: CANARY_TAG,
437
492
  });
438
493
 
439
- packagesToPublish.forEach((pkg) => {
440
- const canaryVersion = canaryVersions.get(pkg.name);
441
- console.log(`āœ… Published ${pkg.name}@${canaryVersion}`);
442
- });
443
- publishSuccess = true;
494
+ // Only use package names from the report summary, not versions.
495
+ // pnpm-publish-summary.json reports the version from the workspace
496
+ // package.json, which is wrong for packages with publishConfig.directory.
497
+ for (const { name } of publishedPackages) {
498
+ publishedNames.add(name);
499
+ console.log(`āœ… Published ${name}@${canaryVersions.get(name)}`);
500
+ }
444
501
  } finally {
445
502
  // Always restore original package.json files in parallel
446
503
  console.log('\nšŸ”„ Restoring original package.json files...');
@@ -454,16 +511,26 @@ async function publishCanaryVersions(
454
511
  await Promise.all(restorePromises);
455
512
  }
456
513
 
457
- if (publishSuccess) {
458
- // Create/update the canary tag after successful publish
459
- await createCanaryTag(options.dryRun);
460
-
461
- // Create GitHub releases if requested
514
+ if (publishedNames.size > 0) {
515
+ // Create GitHub releases only for actually published packages
462
516
  if (options.githubRelease) {
463
- await createGitHubReleasesForPackages(packagesToPublish, canaryVersions, changelogs, options);
517
+ const actuallyPublished = packagesToPublish.filter((pkg) => publishedNames.has(pkg.name));
518
+ await createGitHubReleasesForPackages(actuallyPublished, canaryVersions, changelogs, options);
464
519
  }
465
520
 
466
- console.log('\nšŸŽ‰ All canary versions published successfully!');
521
+ // Only advance the canary tag if all expected packages were published.
522
+ // Otherwise the tag would skip over unpublished packages and they'd
523
+ // never be retried on the next run.
524
+ const missing = packagesToPublish.filter((pkg) => !publishedNames.has(pkg.name));
525
+ if (missing.length === 0) {
526
+ await createCanaryTag(options.dryRun);
527
+ console.log('\nšŸŽ‰ All canary versions published successfully!');
528
+ } else {
529
+ const missingNames = missing.map((pkg) => pkg.name).join(', ');
530
+ console.warn(
531
+ `\nāš ļø Canary tag not advanced, some packages failed to publish: ${missingNames}`,
532
+ );
533
+ }
467
534
  }
468
535
  }
469
536
 
@@ -520,9 +587,14 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
520
587
  const { issues } = await validatePublishDependencies(filteredPackages);
521
588
 
522
589
  if (issues.length > 0) {
523
- throw new Error('Invalid dependencies structure of packages to be published.', {
524
- cause: issues,
525
- });
590
+ throw new Error(
591
+ `Invalid dependencies structure of packages to be published -
592
+ ${issues.join('\n ')}
593
+ `,
594
+ {
595
+ cause: issues,
596
+ },
597
+ );
526
598
  }
527
599
 
528
600
  console.log('āœ… All workspace dependency requirements satisfied');
@@ -11,25 +11,44 @@ import { findWorkspaceDir } from '@pnpm/find-workspace-dir';
11
11
 
12
12
  import { getRepositoryInfo } from '../utils/git.mjs';
13
13
  import { toPosixPath } from '../utils/path.mjs';
14
- import { getWorkspacePackages } from '../utils/pnpm.mjs';
14
+ import { getPackagesNeedingManualPublish, getWorkspacePackages } from '../utils/pnpm.mjs';
15
15
 
16
16
  /**
17
17
  * @typedef {Object} Args
18
18
  * @property {boolean} [dryRun] If true, will only log the commands without executing them
19
+ * @property {string} [otp] 6 digit auth code to forward to npm for two-factor authentication
19
20
  */
20
21
 
21
22
  export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
22
23
  command: 'publish-new-package [pkg...]',
23
24
  describe: 'Publish new empty package(s) to the npm registry.',
24
25
  builder: (yargs) =>
25
- yargs.option('dryRun', {
26
- type: 'boolean',
27
- default: false,
28
- description: 'If true, will only log the commands without executing them.',
29
- }),
26
+ yargs
27
+ .option('dryRun', {
28
+ type: 'boolean',
29
+ default: false,
30
+ description: 'If true, will only log the commands without executing them.',
31
+ })
32
+ .option('otp', {
33
+ type: 'string',
34
+ description: '6 digit auth code to forward to npm for two-factor authentication.',
35
+ coerce: (value) => {
36
+ if (value === undefined) {
37
+ return value;
38
+ }
39
+
40
+ if (!/^\d{6}$/.test(value)) {
41
+ throw new Error('The --otp option must be a 6 digit number.');
42
+ }
43
+
44
+ return value;
45
+ },
46
+ }),
30
47
  async handler(args) {
31
48
  console.log(`šŸ” Detecting new packages to publish in workspace...`);
32
- const newPackages = await getWorkspacePackages({ nonPublishedOnly: true });
49
+ const newPackages = await getPackagesNeedingManualPublish(
50
+ await getWorkspacePackages({ publicOnly: true }),
51
+ );
33
52
 
34
53
  if (!newPackages.length) {
35
54
  console.log('No new packages to publish.');
@@ -62,7 +81,7 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
62
81
  version: '0.0.1',
63
82
  repository: {
64
83
  type: 'git',
65
- url: `git+https://github.com/${repo.owner}/${repo.remoteName}.git`,
84
+ url: `git+https://github.com/${repo.owner}/${repo.repo}.git`,
66
85
  directory: toPosixPath(path.relative(workspaceDir, pkg.path)),
67
86
  },
68
87
  };
@@ -78,8 +97,12 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
78
97
  if (args.dryRun) {
79
98
  publishArgs.push('--dry-run');
80
99
  }
100
+ if (args.otp) {
101
+ publishArgs.push('--otp', args.otp);
102
+ }
81
103
  await $({
82
104
  cwd: newPkgDir,
105
+ stdio: 'inherit',
83
106
  })`npm publish --access public --tag=canary ${publishArgs}`;
84
107
  console.log(
85
108
  `āœ… ${args.dryRun ? '[Dry run] ' : ''}Published ${chalk.bold(`${pkg.name}@${packageJson.version}`)} to npm registry.`,
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+
3
+ /* eslint-disable no-console */
4
+
5
+ import chalk from 'chalk';
6
+
7
+ import { getWorkspacePackages, renameWorkspaceScope } from '../utils/pnpm.mjs';
8
+
9
+ /**
10
+ * A scope is a single `@`-prefixed segment. Letting a `/` through would build
11
+ * names like `@acme/private/pkg`, which only npm rejects, long after every
12
+ * matching manifest has been rewritten.
13
+ */
14
+ const SCOPE_PATTERN = /^@[a-z0-9-~][a-z0-9-._~]*$/;
15
+
16
+ /**
17
+ * @typedef {Object} Args
18
+ * @property {[string, string]} alias Source and target scope
19
+ */
20
+
21
+ /**
22
+ * @param {string} alias
23
+ * @returns {[string, string]}
24
+ */
25
+ export function parseAlias(alias) {
26
+ const parts = alias.split(':');
27
+ const [from, to] = parts;
28
+ if (parts.length !== 2 || !SCOPE_PATTERN.test(from) || !SCOPE_PATTERN.test(to)) {
29
+ throw new Error(
30
+ `Invalid scope mapping "${alias}". Expected exactly two npm scopes separated by a colon, e.g. "@acme:@acme-private".`,
31
+ );
32
+ }
33
+ if (from === to) {
34
+ throw new Error(`Invalid scope mapping "${alias}". The source and target scope are the same.`);
35
+ }
36
+ return [from, to];
37
+ }
38
+
39
+ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
40
+ command: 'rename-scope <alias>',
41
+ describe: 'Move publishable workspace packages to a different npm scope',
42
+ builder: (yargs) =>
43
+ yargs
44
+ .positional('alias', {
45
+ type: 'string',
46
+ describe: 'Scope mapping written as "@from:@to"',
47
+ coerce: parseAlias,
48
+ })
49
+ .example(
50
+ '$0 rename-scope @acme:@acme-private',
51
+ 'Publish the workspace @acme packages under @acme-private',
52
+ ),
53
+ handler: async (argv) => {
54
+ const [from, to] = argv.alias;
55
+ const packages = await getWorkspacePackages();
56
+ const renamed = await renameWorkspaceScope(packages, from, to);
57
+
58
+ // Matching nothing means the mapping is wrong or stale. Carrying on would
59
+ // publish under the original scope, so fail instead of reporting success.
60
+ if (renamed.size === 0) {
61
+ throw new Error(
62
+ `No publishable workspace packages found in ${from}. Check the scope mapping "${from}:${to}", or the workspace may already have been renamed.`,
63
+ );
64
+ }
65
+
66
+ for (const [oldName, newName] of renamed) {
67
+ console.log(`šŸ“¦ ${oldName} → ${chalk.bold(newName)}`);
68
+ }
69
+
70
+ console.log(
71
+ chalk.yellow(
72
+ '\nāš ļø Workspace manifests were rewritten in place and are not restored. Discard them before committing.',
73
+ ),
74
+ );
75
+ },
76
+ });
@@ -1,11 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import fs from 'node:fs/promises';
4
- import os from 'node:os';
5
- import path from 'node:path';
6
3
  import * as semver from 'semver';
7
4
  import { $ } from 'execa';
8
- import { resolveVersion, findDependencyVersionFromSpec } from '../utils/pnpm.mjs';
5
+ import { findWorkspaceDir } from '@pnpm/find-workspace-dir';
6
+ import {
7
+ resolveVersion,
8
+ findDependencyVersionFromSpec,
9
+ writeOverridesToWorkspace,
10
+ } from '../utils/pnpm.mjs';
9
11
 
10
12
  /**
11
13
  * @typedef {Object} Args
@@ -103,11 +105,8 @@ async function handler(args) {
103
105
  // eslint-disable-next-line no-console
104
106
  console.log(`Using overrides: ${JSON.stringify(overrides, null, 2)}`);
105
107
 
106
- const packageJsonPath = path.resolve(process.cwd(), 'package.json');
107
- const packageJson = JSON.parse(await fs.readFile(packageJsonPath, { encoding: 'utf8' }));
108
- packageJson.resolutions ??= {};
109
- Object.assign(packageJson.resolutions, overrides);
110
- await fs.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}${os.EOL}`);
108
+ const workspaceDir = (await findWorkspaceDir(process.cwd())) ?? process.cwd();
109
+ await writeOverridesToWorkspace(workspaceDir, overrides);
111
110
 
112
111
  await $({ stdio: 'inherit' })`pnpm dedupe`;
113
112
  }