@mui/internal-code-infra 0.0.4-canary.2 → 0.0.4-canary.20

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.
@@ -16,10 +16,12 @@ import * as semver from 'semver';
16
16
 
17
17
  import {
18
18
  getPackageVersionInfo,
19
+ getTransitiveDependencies,
19
20
  getWorkspacePackages,
20
21
  publishPackages,
21
22
  readPackageJson,
22
23
  semverMax,
24
+ validatePublishDependencies,
23
25
  writePackageJson,
24
26
  } from '../utils/pnpm.mjs';
25
27
  import { getCurrentGitSha, getRepositoryInfo } from '../utils/git.mjs';
@@ -28,6 +30,7 @@ import { getCurrentGitSha, getRepositoryInfo } from '../utils/git.mjs';
28
30
  * @typedef {Object} Args
29
31
  * @property {boolean} [dryRun] - Whether to run in dry-run mode
30
32
  * @property {boolean} [githubRelease] - Whether to create GitHub releases for canary packages
33
+ * @property {string[]} [filter] - Same as filtering packages with --filter in pnpm. Only publish packages matching the filter. See https://pnpm.io/filtering.
31
34
  */
32
35
 
33
36
  const CANARY_TAG = 'canary';
@@ -118,80 +121,6 @@ function cleanupCommitMessage(message) {
118
121
  return `${prefix}${msg}`.trim();
119
122
  }
120
123
 
121
- async function getPackageToDependencyMap() {
122
- /**
123
- * @type {(PublicPackage & { dependencies: Record<string, unknown>; private: boolean; })[]}
124
- */
125
- const packagesWithDeps = JSON.parse(
126
- (await $`pnpm ls -r --json --exclude-peers --only-projects --prod`).stdout,
127
- );
128
- /** @type {Record<string, string[]>} */
129
- const directPkgDependencies = packagesWithDeps
130
- .filter((pkg) => !pkg.private)
131
- .reduce((acc, pkg) => {
132
- if (!pkg.name) {
133
- return acc;
134
- }
135
- const deps = Object.keys(pkg.dependencies || {});
136
- if (!deps.length) {
137
- return acc;
138
- }
139
- acc[pkg.name] = deps;
140
- return acc;
141
- }, /** @type {Record<string, string[]>} */ ({}));
142
- return directPkgDependencies;
143
- }
144
-
145
- /**
146
- * @param {Record<string, string[]>} pkgGraph
147
- */
148
- function resolveTransitiveDependencies(pkgGraph = {}) {
149
- // Compute transitive (nested) dependencies limited to workspace packages and avoid cycles.
150
- const workspacePkgNames = new Set(Object.keys(pkgGraph));
151
- const nestedMap = /** @type {Record<string, string[]>} */ ({});
152
-
153
- /**
154
- *
155
- * @param {string} pkgName
156
- * @returns {string[]}
157
- */
158
- const getTransitiveDeps = (pkgName) => {
159
- /**
160
- * @type {Set<string>}
161
- */
162
- const seen = new Set();
163
- const stack = (pkgGraph[pkgName] || []).slice();
164
-
165
- while (stack.length) {
166
- const dep = stack.pop();
167
- if (!dep || seen.has(dep)) {
168
- continue;
169
- }
170
- // Only consider workspace packages for transitive expansion
171
- if (!workspacePkgNames.has(dep)) {
172
- // still record external deps as direct deps but don't traverse into them
173
- seen.add(dep);
174
- continue;
175
- }
176
- seen.add(dep);
177
- const children = pkgGraph[dep] || [];
178
- for (const c of children) {
179
- if (!seen.has(c)) {
180
- stack.push(c);
181
- }
182
- }
183
- }
184
-
185
- return Array.from(seen);
186
- };
187
-
188
- for (const name of Object.keys(pkgGraph)) {
189
- nestedMap[name] = getTransitiveDeps(name);
190
- }
191
-
192
- return nestedMap;
193
- }
194
-
195
124
  /**
196
125
  * Prepare changelog data for packages using GitHub API
197
126
  * @param {PublicPackage[]} packagesToPublish - Packages that will be published
@@ -224,14 +153,23 @@ async function prepareChangelogsFromGitCli(packagesToPublish, allPackages, canar
224
153
  }
225
154
  }),
226
155
  );
227
- // Second pass: check for dependency updates in other packages not part of git history
228
- const pkgDependencies = await getPackageToDependencyMap();
229
- const transitiveDependencies = resolveTransitiveDependencies(pkgDependencies);
156
+ // Second pass: check for dependency updates in other packages not part of git history.
157
+ const workspacePathByName = new Map(allPackages.map((pkg) => [pkg.name, pkg.path]));
158
+ const publishedNames = new Set(packagesToPublish.map((p) => p.name));
159
+
160
+ const transitiveDepSets = await Promise.all(
161
+ allPackages.map((pkg) =>
162
+ getTransitiveDependencies([pkg.name], {
163
+ includeDev: false,
164
+ workspacePathByName,
165
+ }),
166
+ ),
167
+ );
230
168
 
231
169
  for (let i = 0; i < allPackages.length; i += 1) {
232
170
  const pkg = allPackages[i];
233
- const depsToPublish = (transitiveDependencies[pkg.name] ?? []).filter((dep) =>
234
- packagesToPublish.some((p) => p.name === dep),
171
+ const depsToPublish = [...transitiveDepSets[i]].filter(
172
+ (dep) => dep !== pkg.name && publishedNames.has(dep),
235
173
  );
236
174
  if (depsToPublish.length === 0) {
237
175
  continue;
@@ -380,7 +318,14 @@ async function getLastCanaryTag() {
380
318
  // Tag might not exist locally, which is fine
381
319
  }
382
320
 
383
- await $`git fetch origin tag ${CANARY_TAG}`;
321
+ try {
322
+ await $`git fetch origin tag ${CANARY_TAG}`;
323
+ } catch (err) {
324
+ // Tag might not exist on the remote yet (first canary run), which is fine
325
+ if (!(/** @type {Error} */ (err).message?.includes("couldn't find remote ref"))) {
326
+ throw err;
327
+ }
328
+ }
384
329
  const { stdout: remoteCanaryTag } = await $`git ls-remote --tags origin ${CANARY_TAG}`;
385
330
  return remoteCanaryTag.trim() ? CANARY_TAG : null;
386
331
  }
@@ -495,7 +440,11 @@ async function publishCanaryVersions(
495
440
  let publishSuccess = false;
496
441
  try {
497
442
  console.log(`šŸ“¤ Publishing ${packagesToPublish.length} canary versions...`);
498
- await publishPackages(packagesToPublish, { ...options, noGitChecks: true, tag: CANARY_TAG });
443
+ await publishPackages(packagesToPublish, {
444
+ dryRun: options.dryRun,
445
+ noGitChecks: true,
446
+ tag: CANARY_TAG,
447
+ });
499
448
 
500
449
  packagesToPublish.forEach((pkg) => {
501
450
  const canaryVersion = canaryVersions.get(pkg.name);
@@ -542,10 +491,16 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
542
491
  type: 'boolean',
543
492
  default: false,
544
493
  description: 'Create GitHub releases for published packages',
494
+ })
495
+ .option('filter', {
496
+ type: 'string',
497
+ array: true,
498
+ description:
499
+ 'Same as filtering packages with --filter in pnpm. Only publish packages matching the filter. See https://pnpm.io/filtering.',
545
500
  });
546
501
  },
547
502
  handler: async (argv) => {
548
- const { dryRun = false, githubRelease = false } = argv;
503
+ const { dryRun = false, githubRelease = false, filter = [] } = argv;
549
504
 
550
505
  const options = { dryRun, githubRelease };
551
506
 
@@ -557,22 +512,46 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
557
512
  console.log('šŸ“ GitHub releases will be created for published packages\n');
558
513
  }
559
514
 
560
- // Always get all packages first
515
+ // All public packages — needed by publishCanaryVersions to bump versions and update
516
+ // package.json across the entire workspace, even for packages not being published.
561
517
  console.log('šŸ” Discovering all workspace packages...');
562
- const allPackages = await getWorkspacePackages({ publicOnly: true });
518
+ const filteredPackages = await getWorkspacePackages({ publicOnly: true, filter });
563
519
 
564
- if (allPackages.length === 0) {
565
- console.log('āš ļø No public packages found in workspace');
520
+ if (filteredPackages.length === 0) {
521
+ console.log(
522
+ `āš ļø No publishable packages found in workspace${filter.length > 0 ? ` matching filter "${filter.join(', ')}"` : ''}`,
523
+ );
566
524
  return;
567
525
  }
568
526
 
569
- // Check for canary tag to determine selective publishing
527
+ if (filter.length > 0) {
528
+ console.log('šŸ” Validating workspace dependencies for filtered packages...');
529
+
530
+ const { issues } = await validatePublishDependencies(filteredPackages);
531
+
532
+ if (issues.length > 0) {
533
+ throw new Error(
534
+ `Invalid dependencies structure of packages to be published -
535
+ ${issues.join('\n ')}
536
+ `,
537
+ {
538
+ cause: issues,
539
+ },
540
+ );
541
+ }
542
+
543
+ console.log('āœ… All workspace dependency requirements satisfied');
544
+ }
545
+
546
+ // Check for canary tag to determine selective publishing.
547
+ // --filter is applied on top of sinceRef: publish only packages that have
548
+ // changed since the last canary tag AND match the filter.
570
549
  const canaryTag = await getLastCanaryTag();
571
550
 
572
551
  console.log('šŸ” Checking for packages changed since canary tag...');
573
552
  const packages = canaryTag
574
- ? await getWorkspacePackages({ sinceRef: canaryTag, publicOnly: true })
575
- : allPackages;
553
+ ? await getWorkspacePackages({ sinceRef: canaryTag, publicOnly: true, filter })
554
+ : filteredPackages;
576
555
 
577
556
  console.log(`šŸ“‹ Found ${packages.length} packages(s) for canary publishing:`);
578
557
  packages.forEach((pkg) => {
@@ -581,7 +560,7 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
581
560
 
582
561
  // Fetch version info for all packages in parallel
583
562
  console.log('\nšŸ” Fetching package version information...');
584
- const versionInfoPromises = allPackages.map(async (pkg) => {
563
+ const versionInfoPromises = filteredPackages.map(async (pkg) => {
585
564
  const versionInfo = await getPackageVersionInfo(pkg.name, pkg.version);
586
565
  return { packageName: pkg.name, versionInfo };
587
566
  });
@@ -593,7 +572,7 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
593
572
  packageVersionInfo.set(packageName, versionInfo);
594
573
  }
595
574
 
596
- await publishCanaryVersions(packages, allPackages, packageVersionInfo, options);
575
+ await publishCanaryVersions(packages, filteredPackages, packageVersionInfo, options);
597
576
 
598
577
  console.log('\nšŸ Publishing complete!');
599
578
  },
@@ -16,17 +16,34 @@ import { getWorkspacePackages } from '../utils/pnpm.mjs';
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
49
  const newPackages = await getWorkspacePackages({ nonPublishedOnly: true });
@@ -62,7 +79,7 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
62
79
  version: '0.0.1',
63
80
  repository: {
64
81
  type: 'git',
65
- url: `git+https://github.com/${repo.owner}/${repo.remoteName}.git`,
82
+ url: `git+https://github.com/${repo.owner}/${repo.repo}.git`,
66
83
  directory: toPosixPath(path.relative(workspaceDir, pkg.path)),
67
84
  },
68
85
  };
@@ -78,8 +95,12 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
78
95
  if (args.dryRun) {
79
96
  publishArgs.push('--dry-run');
80
97
  }
98
+ if (args.otp) {
99
+ publishArgs.push('--otp', args.otp);
100
+ }
81
101
  await $({
82
102
  cwd: newPkgDir,
103
+ stdio: 'inherit',
83
104
  })`npm publish --access public --tag=canary ${publishArgs}`;
84
105
  console.log(
85
106
  `āœ… ${args.dryRun ? '[Dry run] ' : ''}Published ${chalk.bold(`${pkg.name}@${packageJson.version}`)} to npm registry.`,
@@ -1,6 +1,7 @@
1
1
  import { includeIgnoreFile, fixupConfigRules } from '@eslint/compat';
2
2
  import eslintJs from '@eslint/js';
3
- import { defineConfig } from 'eslint/config';
3
+ // TODO: change back to 'eslint/config' once https://github.com/eslint/rewrite/issues/425 is fixed
4
+ import { defineConfig } from '@eslint/config-helpers';
4
5
  import prettier from 'eslint-config-prettier/flat';
5
6
  import compatPlugin from 'eslint-plugin-compat';
6
7
  import importPlugin from 'eslint-plugin-import';
@@ -1,5 +1,6 @@
1
1
  import nextjs from '@next/eslint-plugin-next';
2
- import { defineConfig } from 'eslint/config';
2
+ // TODO: change back to 'eslint/config' once https://github.com/eslint/rewrite/issues/425 is fixed
3
+ import { defineConfig } from '@eslint/config-helpers';
3
4
 
4
5
  /**
5
6
  * @returns {import('eslint').Linter.Config[]}
@@ -1,4 +1,5 @@
1
- import { defineConfig } from 'eslint/config';
1
+ // TODO: change back to 'eslint/config' once https://github.com/eslint/rewrite/issues/425 is fixed
2
+ import { defineConfig } from '@eslint/config-helpers';
2
3
  import json from '@eslint/json';
3
4
 
4
5
  /**
@@ -1,4 +1,5 @@
1
- import { defineConfig } from 'eslint/config';
1
+ // TODO: change back to 'eslint/config' once https://github.com/eslint/rewrite/issues/425 is fixed
2
+ import { defineConfig } from '@eslint/config-helpers';
2
3
 
3
4
  const restrictedMethods = ['setTimeout', 'setInterval', 'clearTimeout', 'clearInterval'];
4
5
 
@@ -1,7 +1,8 @@
1
1
  import mochaPlugin from 'eslint-plugin-mocha';
2
2
  import vitestPlugin from '@vitest/eslint-plugin';
3
3
  import testingLibrary from 'eslint-plugin-testing-library';
4
- import { defineConfig } from 'eslint/config';
4
+ // TODO: change back to 'eslint/config' once https://github.com/eslint/rewrite/issues/425 is fixed
5
+ import { defineConfig } from '@eslint/config-helpers';
5
6
  import globals from 'globals';
6
7
  import * as tseslint from 'typescript-eslint';
7
8
  import { EXTENSION_TS } from './extensions.mjs';