@mui/internal-code-infra 0.0.4-canary.5 → 0.0.4-canary.51

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 (109) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +19 -8
  3. package/build/babel-config.d.mts +11 -3
  4. package/build/brokenLinksChecker/crawlWorker.d.mts +1 -0
  5. package/build/brokenLinksChecker/index.d.mts +45 -2
  6. package/build/changelog/types.d.ts +1 -1
  7. package/build/cli/cmdArgosPush.d.mts +2 -2
  8. package/build/cli/cmdBuild.d.mts +2 -2
  9. package/build/cli/cmdCopyFiles.d.mts +2 -2
  10. package/build/cli/cmdExtractErrorCodes.d.mts +2 -2
  11. package/build/cli/cmdGenerateChangelog.d.mts +2 -2
  12. package/build/cli/cmdGithubAuth.d.mts +2 -2
  13. package/build/cli/cmdListWorkspaces.d.mts +4 -2
  14. package/build/cli/cmdNetlifyIgnore.d.mts +3 -2
  15. package/build/cli/cmdPublish.d.mts +4 -2
  16. package/build/cli/cmdPublishCanary.d.mts +3 -3
  17. package/build/cli/cmdPublishNewPackage.d.mts +4 -2
  18. package/build/cli/cmdSetVersionOverrides.d.mts +2 -2
  19. package/build/cli/cmdVale.d.mts +46 -0
  20. package/build/cli/cmdValidateBuiltTypes.d.mts +2 -2
  21. package/build/eslint/baseConfig.d.mts +3 -1
  22. package/build/eslint/mui/rules/disallow-react-api-in-server-components.d.mts +2 -2
  23. package/build/eslint/mui/rules/docgen-ignore-before-comment.d.mts +2 -2
  24. package/build/eslint/mui/rules/no-guarded-throw.d.mts +31 -0
  25. package/build/eslint/mui/rules/no-presentation-role.d.mts +5 -0
  26. package/build/eslint/mui/rules/no-restricted-resolved-imports.d.mts +2 -2
  27. package/build/eslint/mui/rules/nodeEnvUtils.d.mts +18 -0
  28. package/build/markdownlint/duplicate-h1.d.mts +1 -1
  29. package/build/markdownlint/git-diff.d.mts +1 -1
  30. package/build/markdownlint/index.d.mts +1 -1
  31. package/build/markdownlint/straight-quotes.d.mts +1 -1
  32. package/build/markdownlint/table-alignment.d.mts +1 -1
  33. package/build/markdownlint/terminal-language.d.mts +1 -1
  34. package/build/remark/config.d.mts +43 -0
  35. package/build/remark/createLintTester.d.mts +10 -0
  36. package/build/remark/firstBlockHeading.d.mts +4 -0
  37. package/build/remark/gitDiff.d.mts +2 -0
  38. package/build/remark/noSpaceInLinks.d.mts +2 -0
  39. package/build/remark/straightQuotes.d.mts +2 -0
  40. package/build/remark/tableAlignment.d.mts +2 -0
  41. package/build/remark/terminalLanguage.d.mts +2 -0
  42. package/build/utils/babel.d.mts +1 -1
  43. package/build/utils/build.d.mts +4 -4
  44. package/build/utils/github.d.mts +1 -1
  45. package/build/utils/pnpm.d.mts +68 -2
  46. package/build/utils/testUtils.d.mts +7 -0
  47. package/build/utils/typescript.d.mts +2 -2
  48. package/package.json +62 -35
  49. package/src/babel-config.mjs +9 -3
  50. package/src/brokenLinksChecker/__fixtures__/static-site/index.html +1 -0
  51. package/src/brokenLinksChecker/__fixtures__/static-site/invalid-html.html +15 -0
  52. package/src/brokenLinksChecker/crawlWorker.mjs +217 -0
  53. package/src/brokenLinksChecker/index.mjs +217 -164
  54. package/src/brokenLinksChecker/index.test.ts +50 -13
  55. package/src/changelog/categorizeCommits.test.ts +5 -5
  56. package/src/changelog/fetchChangelogs.mjs +6 -2
  57. package/src/changelog/parseCommitLabels.test.ts +5 -5
  58. package/src/changelog/renderChangelog.mjs +1 -1
  59. package/src/changelog/types.ts +1 -1
  60. package/src/cli/cmdListWorkspaces.mjs +9 -2
  61. package/src/cli/cmdNetlifyIgnore.mjs +35 -93
  62. package/src/cli/cmdPublish.mjs +51 -14
  63. package/src/cli/cmdPublishCanary.mjs +128 -132
  64. package/src/cli/cmdPublishNewPackage.mjs +27 -6
  65. package/src/cli/cmdVale.mjs +513 -0
  66. package/src/cli/cmdVale.test.mjs +644 -0
  67. package/src/cli/index.mjs +2 -0
  68. package/src/cli/packageJson.d.ts +1 -1
  69. package/src/eslint/baseConfig.mjs +45 -20
  70. package/src/eslint/docsConfig.mjs +2 -1
  71. package/src/eslint/jsonConfig.mjs +2 -1
  72. package/src/eslint/mui/config.mjs +21 -1
  73. package/src/eslint/mui/index.mjs +4 -0
  74. package/src/eslint/mui/rules/no-guarded-throw.mjs +115 -0
  75. package/src/eslint/mui/rules/no-guarded-throw.test.mjs +206 -0
  76. package/src/eslint/mui/rules/no-presentation-role.mjs +60 -0
  77. package/src/eslint/mui/rules/no-presentation-role.test.mjs +33 -0
  78. package/src/eslint/mui/rules/nodeEnvUtils.mjs +52 -0
  79. package/src/eslint/mui/rules/require-dev-wrapper.mjs +25 -40
  80. package/src/eslint/testConfig.mjs +2 -1
  81. package/src/estree-typescript.d.ts +1 -1
  82. package/src/remark/config.mjs +157 -0
  83. package/src/remark/createLintTester.mjs +19 -0
  84. package/src/remark/firstBlockHeading.mjs +87 -0
  85. package/src/remark/firstBlockHeading.test.mjs +107 -0
  86. package/src/remark/gitDiff.mjs +43 -0
  87. package/src/remark/gitDiff.test.mjs +45 -0
  88. package/src/remark/noSpaceInLinks.mjs +42 -0
  89. package/src/remark/noSpaceInLinks.test.mjs +22 -0
  90. package/src/remark/straightQuotes.mjs +31 -0
  91. package/src/remark/straightQuotes.test.mjs +25 -0
  92. package/src/remark/tableAlignment.mjs +23 -0
  93. package/src/remark/tableAlignment.test.mjs +28 -0
  94. package/src/remark/terminalLanguage.mjs +19 -0
  95. package/src/remark/terminalLanguage.test.mjs +17 -0
  96. package/src/untyped-plugins.d.ts +11 -11
  97. package/src/utils/build.mjs +18 -1
  98. package/src/utils/build.test.mjs +585 -575
  99. package/src/utils/pnpm.mjs +192 -3
  100. package/src/utils/pnpm.test.mjs +580 -0
  101. package/src/utils/testUtils.mjs +18 -0
  102. package/src/utils/typescript.test.mjs +249 -272
  103. package/vale/.vale.ini +1 -0
  104. package/vale/styles/MUI/CorrectReferenceAllCases.yml +43 -0
  105. package/vale/styles/MUI/CorrectRererenceCased.yml +14 -0
  106. package/vale/styles/MUI/GoogleLatin.yml +11 -0
  107. package/vale/styles/MUI/MuiBrandName.yml +22 -0
  108. package/vale/styles/MUI/NoBritish.yml +112 -0
  109. package/vale/styles/MUI/NoCompanyName.yml +17 -0
@@ -210,11 +210,11 @@ describe('parseCommitLabels', () => {
210
210
 
211
211
  describe('category overrides', () => {
212
212
  it('should detect category override labels', () => {
213
- const commit = createCommit({ labels: ['all components'] });
213
+ const commit = createCommit({ labels: ['scope: all components'] });
214
214
  const config: LabelConfig = {
215
215
  ...baseLabelConfig,
216
216
  categoryOverrides: {
217
- 'all components': 'General changes',
217
+ 'scope: all components': 'General changes',
218
218
  },
219
219
  };
220
220
 
@@ -224,11 +224,11 @@ describe('parseCommitLabels', () => {
224
224
  });
225
225
 
226
226
  it('should use the last category override when multiple are present', () => {
227
- const commit = createCommit({ labels: ['all components', 'docs'] });
227
+ const commit = createCommit({ labels: ['scope: all components', 'docs'] });
228
228
  const config: LabelConfig = {
229
229
  ...baseLabelConfig,
230
230
  categoryOverrides: {
231
- 'all components': 'General changes',
231
+ 'scope: all components': 'General changes',
232
232
  docs: 'Documentation',
233
233
  },
234
234
  };
@@ -243,7 +243,7 @@ describe('parseCommitLabels', () => {
243
243
  const config: LabelConfig = {
244
244
  ...baseLabelConfig,
245
245
  categoryOverrides: {
246
- 'all components': 'General changes',
246
+ 'scope: all components': 'General changes',
247
247
  },
248
248
  };
249
249
 
@@ -438,7 +438,7 @@ function renderContributors(contributors, config, lines) {
438
438
  const template = getTemplateString(
439
439
  config.contributors?.message?.contributors,
440
440
  allContributors.length,
441
- `${allContributors.length !== 1 ? 'All contributors of this release in alphabetical order' : 'Contributor of this release'} : {{contributors}}`,
441
+ `${allContributors.length !== 1 ? 'All contributors of this release in alphabetical order' : 'Contributor of this release'}: {{contributors}}`,
442
442
  );
443
443
  const contributorsMessage = templateString(template, {
444
444
  contributors: renderContributorsList(allContributors),
@@ -233,7 +233,7 @@ export interface IntroConfig {
233
233
  * - {{teamCount}}: Number of team members
234
234
  * - {{communityCount}}: Number of community contributors
235
235
  *
236
- * Example: "We'd like to extend a big thank you to the {{contributorCount}} contributors who made this release possible"
236
+ * Example: "A big thanks to the {{contributorCount}} contributors who made this release possible."
237
237
  *
238
238
  * Set to `false` or omit to disable the thank you message.
239
239
  */
@@ -15,6 +15,7 @@ import { getWorkspacePackages } from '../utils/pnpm.mjs';
15
15
  * @property {boolean} [publicOnly] - Whether to filter to only public packages
16
16
  * @property {'json'|'path'|'name'|'publish-dir'} [output] - Output format (name, path, or json)
17
17
  * @property {string} [sinceRef] - Git reference to filter changes since
18
+ * @property {string[]} [filter] - Same as filtering packages with --filter in pnpm. Only include packages matching the filter. See https://pnpm.io/filtering.
18
19
  */
19
20
 
20
21
  export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
@@ -37,13 +38,19 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
37
38
  .option('since-ref', {
38
39
  type: 'string',
39
40
  description: 'Filter packages changed since git reference',
41
+ })
42
+ .option('filter', {
43
+ type: 'string',
44
+ array: true,
45
+ description:
46
+ 'Same as filtering packages with --filter in pnpm. Only include packages matching the filter. See https://pnpm.io/filtering.',
40
47
  });
41
48
  },
42
49
  handler: async (argv) => {
43
- const { publicOnly = false, output = 'name', sinceRef } = argv;
50
+ const { publicOnly = false, output = 'name', sinceRef, filter = [] } = argv;
44
51
 
45
52
  // Get packages using our helper function
46
- const packages = await getWorkspacePackages({ sinceRef, publicOnly });
53
+ const packages = await getWorkspacePackages({ sinceRef, publicOnly, filter });
47
54
 
48
55
  if (output === 'json') {
49
56
  // Serialize packages to JSON
@@ -6,111 +6,40 @@
6
6
  * @typedef {Object} Args
7
7
  * @property {string[]} workspaces - List of workspace names to process
8
8
  * @property {boolean} [check] - Check mode - error if the generated content differs from current
9
+ * @property {string} [baseBranch] - Branch to compare PRs against (default: master)
9
10
  */
10
11
 
11
12
  import * as fs from 'node:fs/promises';
12
13
  import * as path from 'node:path';
13
14
  import { findWorkspaceDir } from '@pnpm/find-workspace-dir';
14
15
  import { toPosixPath } from '../utils/path.mjs';
15
- import { getWorkspacePackages } from '../utils/pnpm.mjs';
16
+ import { getTransitiveDependencies, getWorkspacePackages } from '../utils/pnpm.mjs';
16
17
 
17
18
  /**
18
- * Get all workspace dependencies (direct and transitive) from a package
19
- * @param {string} packageName - Package name
20
- * @param {Map<string, string>} workspaceMap - Map of workspace name to path
21
- * @param {Map<string, Promise<Set<string>>>} cache - Cache of package resolution promises
22
- * @returns {Promise<Set<string>>} Set of workspace package names (dependencies only, not including the package itself)
23
- */
24
- async function getWorkspaceDependenciesRecursive(packageName, workspaceMap, cache) {
25
- // Check cache first
26
- const cached = cache.get(packageName);
27
- if (cached) {
28
- return cached;
29
- }
30
-
31
- // Create the resolution promise
32
- const promise = (async () => {
33
- const packagePath = workspaceMap.get(packageName);
34
- if (!packagePath) {
35
- throw new Error(`Workspace "${packageName}" not found in the repository`);
36
- }
37
-
38
- const packageJsonPath = path.join(packagePath, 'package.json');
39
- const content = await fs.readFile(packageJsonPath, 'utf8');
40
- const packageJson = JSON.parse(content);
41
-
42
- // Collect all dependency names
43
- /** @type {Set<string>} */
44
- const allDeps = new Set();
45
- if (packageJson.dependencies) {
46
- Object.keys(packageJson.dependencies).forEach((dep) => allDeps.add(dep));
47
- }
48
- if (packageJson.devDependencies) {
49
- Object.keys(packageJson.devDependencies).forEach((dep) => allDeps.add(dep));
50
- }
51
- if (packageJson.peerDependencies) {
52
- Object.keys(packageJson.peerDependencies).forEach((dep) => allDeps.add(dep));
53
- }
54
-
55
- // Filter to only workspace dependencies
56
- const workspaceDeps = Array.from(allDeps).filter((dep) => workspaceMap.has(dep));
57
-
58
- // Recursively process workspace dependencies in parallel
59
- const recursiveResults = await Promise.all(
60
- workspaceDeps.map(async (dep) => {
61
- return getWorkspaceDependenciesRecursive(dep, workspaceMap, cache);
62
- }),
63
- );
64
-
65
- // Merge all results using flatMap
66
- return new Set(recursiveResults.flatMap((result) => Array.from(result)).concat(workspaceDeps));
67
- })();
68
-
69
- // Store in cache before returning
70
- cache.set(packageName, promise);
71
-
72
- return promise;
73
- }
74
-
75
- /**
76
- * Get transitive workspace dependencies for a list of workspace names
77
- * @param {string[]} workspaceNames - Array of workspace names
78
- * @param {Map<string, string>} workspaceMap - Map of workspace name to path
79
- * @returns {Promise<Set<string>>} Set of workspace package names (including requested packages and all their dependencies)
80
- */
81
- async function getTransitiveDependencies(workspaceNames, workspaceMap) {
82
- // Shared cache for all workspace dependency resolution
83
- const cache = new Map();
84
-
85
- // Validate all workspace names exist
86
- for (const workspaceName of workspaceNames) {
87
- if (!workspaceMap.has(workspaceName)) {
88
- throw new Error(`Workspace "${workspaceName}" not found in the repository`);
89
- }
90
- }
91
-
92
- // Process each requested workspace in parallel
93
- const workspaceResults = await Promise.all(
94
- workspaceNames.map((workspaceName) =>
95
- getWorkspaceDependenciesRecursive(workspaceName, workspaceMap, cache),
96
- ),
97
- );
98
-
99
- // Merge all results using flatMap and add the original package names
100
- return new Set(workspaceNames.concat(workspaceResults.flatMap((result) => Array.from(result))));
101
- }
102
-
103
- /**
104
- * Generate the ignore command string for netlify.toml
19
+ * Generate the ignore command string for netlify.toml.
20
+ *
21
+ * Production builds (Netlify's $CONTEXT === "production" — i.e. any branch
22
+ * configured as the site's production branch, whatever its name) always
23
+ * build, so downstream plugins (e.g. e2e triggers) run on every commit and
24
+ * catch regressions in external dependencies even when the commit doesn't
25
+ * touch the watched paths.
26
+ *
27
+ * Every other context (deploy-preview, branch-deploy) diffs against the
28
+ * merge-base with origin/<baseBranch>. This way a PR rebase whose head
29
+ * commit doesn't touch the watched paths still rebuilds when the PR as a
30
+ * whole introduces changes to them — otherwise downstream plugins silently
31
+ * never run.
32
+ *
105
33
  * @param {string[]} paths - Array of paths to include in the ignore command
106
34
  * @param {string} packagePath - Absolute path to the package directory
107
35
  * @param {string} workspaceRoot - Absolute path to the workspace root
36
+ * @param {string} baseBranch - Branch to compare PRs against
108
37
  * @returns {string} The ignore command string
109
38
  */
110
- function generateIgnoreCommand(paths, packagePath, workspaceRoot) {
39
+ function generateIgnoreCommand(paths, packagePath, workspaceRoot, baseBranch) {
111
40
  const relFromBase = `${toPosixPath(path.relative(packagePath, workspaceRoot))}/`;
112
41
  const pathsStr = paths.join(' ');
113
- return ` ignore = "cd ${relFromBase} && git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF ${pathsStr}"`;
42
+ return ` ignore = """cd ${relFromBase} && [ "$CONTEXT" != "production" ] && git fetch origin ${baseBranch} --depth=500 -q && git diff --quiet FETCH_HEAD...$COMMIT_REF -- ${pathsStr}"""`;
114
43
  }
115
44
 
116
45
  /**
@@ -180,6 +109,12 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
180
109
  default: false,
181
110
  describe: 'Check if the netlify.toml needs updating without modifying it',
182
111
  })
112
+ .option('base-branch', {
113
+ type: 'string',
114
+ default: 'master',
115
+ describe:
116
+ "Branch to compare PRs against (the site's production branch on Netlify). Production builds always rebuild regardless of this value.",
117
+ })
183
118
  .example('$0 netlify-ignore @mui/internal-docs-infra', 'Update netlify.toml for a workspace')
184
119
  .example(
185
120
  '$0 netlify-ignore @mui/internal-docs-infra @mui/internal-code-infra',
@@ -191,7 +126,7 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
191
126
  );
192
127
  },
193
128
  handler: async (argv) => {
194
- const { workspaces, check = false } = argv;
129
+ const { workspaces, check = false, baseBranch = 'master' } = argv;
195
130
 
196
131
  // Get the workspace root
197
132
  const workspaceRoot = await findWorkspaceDir(process.cwd());
@@ -220,7 +155,9 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
220
155
  console.log(`Processing ${workspaceName}...`);
221
156
 
222
157
  // Get transitive dependencies for this specific workspace
223
- const dependencyNames = await getTransitiveDependencies([workspaceName], workspaceMap);
158
+ const dependencyNames = await getTransitiveDependencies([workspaceName], {
159
+ workspacePathByName: workspaceMap,
160
+ });
224
161
 
225
162
  // Convert package names to relative paths (normalize to POSIX separators for git)
226
163
  const relativePaths = Array.from(dependencyNames)
@@ -241,7 +178,12 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
241
178
  const allPaths = [...relativePaths, 'pnpm-lock.yaml'];
242
179
 
243
180
  // Generate the ignore command for this workspace
244
- const newIgnoreCommand = generateIgnoreCommand(allPaths, workspacePath, workspaceRoot);
181
+ const newIgnoreCommand = generateIgnoreCommand(
182
+ allPaths,
183
+ workspacePath,
184
+ workspaceRoot,
185
+ baseBranch,
186
+ );
245
187
 
246
188
  // Update or check the netlify.toml file
247
189
  await updateNetlifyToml(tomlPath, newIgnoreCommand, check);
@@ -5,6 +5,7 @@
5
5
  /**
6
6
  * @typedef {import('../utils/pnpm.mjs').PublicPackage} PublicPackage
7
7
  * @typedef {import('../utils/pnpm.mjs').PublishOptions} PublishOptions
8
+ * @typedef {import('../utils/pnpm.mjs').PublishSummaryEntry} PublishSummaryEntry
8
9
  */
9
10
 
10
11
  import select from '@inquirer/select';
@@ -17,7 +18,11 @@ import * as fs from 'node:fs/promises';
17
18
  import * as semver from 'semver';
18
19
 
19
20
  import { persistentAuthStrategy } from '../utils/github.mjs';
20
- import { getWorkspacePackages, publishPackages } from '../utils/pnpm.mjs';
21
+ import {
22
+ getWorkspacePackages,
23
+ publishPackages,
24
+ validatePublishDependencies,
25
+ } from '../utils/pnpm.mjs';
21
26
  import { getCurrentGitSha, getRepositoryInfo } from '../utils/git.mjs';
22
27
 
23
28
  const isCI = envCI().isCi;
@@ -33,6 +38,7 @@ function getOctokit() {
33
38
  * @property {string} tag NPM dist tag to publish to
34
39
  * @property {boolean} ci Runs in CI environment
35
40
  * @property {string} [sha] Git SHA to use for the GitHub release workflow (local only)
41
+ * @property {string[]} [filter] Same as filtering packages with --filter in pnpm. Only publish packages matching the filter. See https://pnpm.io/filtering.
36
42
  */
37
43
 
38
44
  /**
@@ -189,18 +195,11 @@ async function validateGitHubRelease(version) {
189
195
  * Publish packages to npm
190
196
  * @param {PublicPackage[]} packages - Packages to publish
191
197
  * @param {PublishOptions} options - Publishing options
192
- * @returns {Promise<void>}
198
+ * @returns {Promise<PublishSummaryEntry[]>}
193
199
  */
194
200
  async function publishToNpm(packages, options) {
195
- console.log('\nšŸ“¦ Publishing packages to npm...');
196
- console.log(`šŸ“‹ Found ${packages.length} packages:`);
197
- packages.forEach((pkg) => {
198
- console.log(` • ${pkg.name}@${pkg.version}`);
199
- });
200
-
201
201
  // Use pnpm's built-in duplicate checking - no need to check versions ourselves
202
- await publishPackages(packages, options);
203
- console.log('āœ… Successfully published to npm');
202
+ return publishPackages(packages, options);
204
203
  }
205
204
 
206
205
  /**
@@ -260,10 +259,16 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
260
259
  .option('sha', {
261
260
  type: 'string',
262
261
  description: 'Git SHA to use for the GitHub release workflow (local only)',
262
+ })
263
+ .option('filter', {
264
+ type: 'string',
265
+ array: true,
266
+ description:
267
+ 'Same as filtering packages with --filter in pnpm. Only publish packages matching the filter. See https://pnpm.io/filtering.',
263
268
  });
264
269
  },
265
270
  handler: async (argv) => {
266
- const { dryRun = false, githubRelease = false, tag = 'latest', sha } = argv;
271
+ const { dryRun = false, githubRelease = false, tag = 'latest', sha, filter = [] } = argv;
267
272
 
268
273
  if (isCI && !argv.ci) {
269
274
  console.error(
@@ -290,13 +295,34 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
290
295
  // Get all packages
291
296
  console.log('šŸ” Discovering all workspace packages...');
292
297
 
293
- const allPackages = await getWorkspacePackages({ publicOnly: true });
298
+ const allPackages = await getWorkspacePackages({ publicOnly: true, filter });
294
299
 
295
300
  if (allPackages.length === 0) {
296
- console.log('āš ļø No public packages found in workspace');
301
+ console.log(
302
+ `āš ļø No publishable packages found in workspace${filter.length > 0 ? ` matching filter "${filter.join(', ')}"` : ''}`,
303
+ );
297
304
  return;
298
305
  }
299
306
 
307
+ if (filter.length > 0) {
308
+ console.log('šŸ” Validating workspace dependencies for filtered packages...');
309
+
310
+ const { issues } = await validatePublishDependencies(allPackages);
311
+
312
+ if (issues.length > 0) {
313
+ throw new Error(
314
+ `Invalid dependencies structure of packages to be published -
315
+ ${issues.join('\n ')}
316
+ `,
317
+ {
318
+ cause: issues,
319
+ },
320
+ );
321
+ }
322
+
323
+ console.log('āœ… All workspace dependency requirements satisfied');
324
+ }
325
+
300
326
  // Get version from root package.json
301
327
  const version = await getReleaseVersion();
302
328
 
@@ -323,7 +349,18 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
323
349
 
324
350
  // Publish to npm (pnpm handles duplicate checking automatically)
325
351
  // No git checks, we'll do our own
326
- await publishToNpm(allPackages, { dryRun, noGitChecks: true, tag });
352
+ console.log('\nšŸ“¦ Publishing packages to npm...');
353
+ const publishedPackages = await publishToNpm(allPackages, { dryRun, noGitChecks: true, tag });
354
+
355
+ if (publishedPackages.length === 0) {
356
+ console.log('ā„¹ļø No packages were published (all may already be up to date on npm)');
357
+ console.log('\nšŸ Nothing to publish, skipping git tag and GitHub release.');
358
+ return;
359
+ }
360
+
361
+ publishedPackages.forEach((pkg) => {
362
+ console.log(`āœ… Published ${pkg.name}@${pkg.version}`);
363
+ });
327
364
 
328
365
  await createGitTag(version, dryRun);
329
366