@mui/internal-code-infra 0.0.4-canary.61 → 0.0.4-canary.63

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.
@@ -135,6 +135,19 @@ export declare function readPackageJson(packagePath: string): Promise<import('..
135
135
  * @returns {Promise<void>}
136
136
  */
137
137
  export declare function writePackageJson(packagePath: string, packageJson: Object): Promise<void>;
138
+ /**
139
+ * Write the computed overrides into whichever manifest already defines
140
+ * overrides — preferring pnpm-workspace.yaml, falling back to package.json
141
+ * `pnpm.overrides`, defaulting to pnpm-workspace.yaml — and persist the result.
142
+ * A missing pnpm-workspace.yaml is treated as empty, so a fresh file is created
143
+ * with just the `overrides:` block. Rejects a `resolutions` field in
144
+ * package.json because pnpm 11 silently ignores it.
145
+ *
146
+ * @param {string} workspaceDir - Workspace root directory
147
+ * @param {Record<string, string>} overrides - Overrides to apply
148
+ * @returns {Promise<void>}
149
+ */
150
+ export declare function writeOverridesToWorkspace(workspaceDir: string, overrides: Record<string, string>): Promise<void>;
138
151
  /**
139
152
  * Resolve a package@version specifier to an exact version
140
153
  * @param {string} packageSpec - Package specifier in format "package@version"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/internal-code-infra",
3
- "version": "0.0.4-canary.61",
3
+ "version": "0.0.4-canary.63",
4
4
  "author": "MUI Team",
5
5
  "description": "Infra scripts and configs to be used across MUI repos.",
6
6
  "license": "MIT",
@@ -138,6 +138,7 @@
138
138
  "unified": "^11.0.5",
139
139
  "unified-lint-rule": "^3.0.1",
140
140
  "unist-util-visit": "^5.1.0",
141
+ "yaml": "^2.9.0",
141
142
  "yargs": "^18.0.0",
142
143
  "@mui/internal-babel-plugin-display-name": "1.0.4-canary.20",
143
144
  "@mui/internal-babel-plugin-minify-errors": "2.0.8-canary.27",
@@ -191,7 +192,7 @@
191
192
  "publishConfig": {
192
193
  "access": "public"
193
194
  },
194
- "gitSha": "6c77485e850c4bb78f9c999a3483093e2bc79c06",
195
+ "gitSha": "e08081fbb9ce12311a1acb555f69108dc1231951",
195
196
  "scripts": {
196
197
  "build": "tsgo -p tsconfig.build.json",
197
198
  "typescript": "tsgo -noEmit",
@@ -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
  }
@@ -1281,4 +1281,77 @@ describe('createPackageImports', () => {
1281
1281
  },
1282
1282
  });
1283
1283
  });
1284
+
1285
+ it('passes a conditions object of bare specifiers through unchanged, preserving condition order', async () => {
1286
+ const cwd = await makeTempDir();
1287
+
1288
+ const imports = await createPackageImports(
1289
+ {
1290
+ '#mui/TransitionGroupContext': {
1291
+ node: 'react-transition-group/cjs/TransitionGroupContext.js',
1292
+ browser: 'react-transition-group/esm/TransitionGroupContext.js',
1293
+ default: 'react-transition-group/esm/TransitionGroupContext.js',
1294
+ },
1295
+ },
1296
+ {
1297
+ bundles: [
1298
+ { type: 'esm', dir: '.' },
1299
+ { type: 'cjs', dir: '.' },
1300
+ ],
1301
+ cwd,
1302
+ isFlat: true,
1303
+ packageType: 'module',
1304
+ },
1305
+ );
1306
+
1307
+ expect(imports).toEqual({
1308
+ '#mui/TransitionGroupContext': {
1309
+ node: 'react-transition-group/cjs/TransitionGroupContext.js',
1310
+ browser: 'react-transition-group/esm/TransitionGroupContext.js',
1311
+ default: 'react-transition-group/esm/TransitionGroupContext.js',
1312
+ },
1313
+ });
1314
+ expect(
1315
+ Object.keys(
1316
+ /** @type {Record<string, unknown>} */ (imports?.['#mui/TransitionGroupContext']),
1317
+ ),
1318
+ ).toEqual(['node', 'browser', 'default']);
1319
+ });
1320
+
1321
+ it('passes a nested import/require/default object of bare specifiers through unchanged', async () => {
1322
+ const cwd = await makeTempDir();
1323
+
1324
+ const imports = await createPackageImports(
1325
+ {
1326
+ '#mui/TransitionGroupContext': {
1327
+ node: 'react-transition-group/cjs/TransitionGroupContext.js',
1328
+ default: {
1329
+ import: 'react-transition-group/esm/TransitionGroupContext.js',
1330
+ require: 'react-transition-group/cjs/TransitionGroupContext.js',
1331
+ default: 'react-transition-group/esm/TransitionGroupContext.js',
1332
+ },
1333
+ },
1334
+ },
1335
+ {
1336
+ bundles: [
1337
+ { type: 'esm', dir: '.' },
1338
+ { type: 'cjs', dir: '.' },
1339
+ ],
1340
+ cwd,
1341
+ isFlat: true,
1342
+ packageType: 'module',
1343
+ },
1344
+ );
1345
+
1346
+ expect(imports).toEqual({
1347
+ '#mui/TransitionGroupContext': {
1348
+ node: 'react-transition-group/cjs/TransitionGroupContext.js',
1349
+ default: {
1350
+ import: 'react-transition-group/esm/TransitionGroupContext.js',
1351
+ require: 'react-transition-group/cjs/TransitionGroupContext.js',
1352
+ default: 'react-transition-group/esm/TransitionGroupContext.js',
1353
+ },
1354
+ },
1355
+ });
1356
+ });
1284
1357
  });
@@ -5,6 +5,7 @@ import { $ } from 'execa';
5
5
  import * as fs from 'node:fs/promises';
6
6
  import * as path from 'node:path';
7
7
  import * as semver from 'semver';
8
+ import { parseDocument, isMap } from 'yaml';
8
9
 
9
10
  /**
10
11
  * @typedef {Object} PrivatePackage
@@ -385,6 +386,68 @@ export async function writePackageJson(packagePath, packageJson) {
385
386
  await fs.writeFile(path.join(packagePath, 'package.json'), content);
386
387
  }
387
388
 
389
+ /**
390
+ * Write the computed overrides into whichever manifest already defines
391
+ * overrides — preferring pnpm-workspace.yaml, falling back to package.json
392
+ * `pnpm.overrides`, defaulting to pnpm-workspace.yaml — and persist the result.
393
+ * A missing pnpm-workspace.yaml is treated as empty, so a fresh file is created
394
+ * with just the `overrides:` block. Rejects a `resolutions` field in
395
+ * package.json because pnpm 11 silently ignores it.
396
+ *
397
+ * @param {string} workspaceDir - Workspace root directory
398
+ * @param {Record<string, string>} overrides - Overrides to apply
399
+ * @returns {Promise<void>}
400
+ */
401
+ export async function writeOverridesToWorkspace(workspaceDir, overrides) {
402
+ const workspaceYamlPath = path.join(workspaceDir, 'pnpm-workspace.yaml');
403
+ const yamlPromise = fs.readFile(workspaceYamlPath, { encoding: 'utf8' }).catch((error) => {
404
+ if (/** @type {NodeJS.ErrnoException} */ (error).code !== 'ENOENT') {
405
+ throw error;
406
+ }
407
+ return '';
408
+ });
409
+ const [rootPackageJson, yamlSource] = await Promise.all([
410
+ readPackageJson(workspaceDir),
411
+ yamlPromise,
412
+ ]);
413
+
414
+ const { resolutions } = rootPackageJson;
415
+ if (resolutions && Object.keys(resolutions).length > 0) {
416
+ throw new Error(
417
+ 'Found a "resolutions" field in package.json. pnpm 11 ignores it silently. ' +
418
+ 'Move those entries into the "overrides:" key of pnpm-workspace.yaml.',
419
+ );
420
+ }
421
+
422
+ // Parsed once, reused for both the read (does it have overrides?) and the write.
423
+ const doc = parseDocument(yamlSource);
424
+ const existing = doc.get('overrides');
425
+ const workspaceHasOverrides = isMap(existing) && existing.items.length > 0;
426
+
427
+ const pnpm = /** @type {{ overrides?: Record<string, string> } | undefined} */ (
428
+ rootPackageJson.pnpm
429
+ );
430
+ const packageJsonOverrides = pnpm?.overrides;
431
+
432
+ // Write where overrides already live; default to the workspace file.
433
+ if (
434
+ !workspaceHasOverrides &&
435
+ packageJsonOverrides &&
436
+ Object.keys(packageJsonOverrides).length > 0
437
+ ) {
438
+ await writePackageJson(workspaceDir, {
439
+ ...rootPackageJson,
440
+ pnpm: { ...pnpm, overrides: { ...packageJsonOverrides, ...overrides } },
441
+ });
442
+ return;
443
+ }
444
+
445
+ for (const [name, version] of Object.entries(overrides)) {
446
+ doc.setIn(['overrides', name], version);
447
+ }
448
+ await fs.writeFile(workspaceYamlPath, doc.toString());
449
+ }
450
+
388
451
  /**
389
452
  * Resolve a package@version specifier to an exact version
390
453
  * @param {string} packageSpec - Package specifier in format "package@version"
@@ -3,7 +3,12 @@ import * as path from 'node:path';
3
3
  import { describe, it, expect } from 'vitest';
4
4
 
5
5
  import { makeTempDir } from './testUtils.mjs';
6
- import { checkPublishDependencies } from './pnpm.mjs';
6
+ import {
7
+ checkPublishDependencies,
8
+ readPackageJson,
9
+ writePackageJson,
10
+ writeOverridesToWorkspace,
11
+ } from './pnpm.mjs';
7
12
 
8
13
  /**
9
14
  * Write a package.json file to a temp subdirectory and return the directory path.
@@ -578,3 +583,149 @@ describe('checkPublishDependencies', () => {
578
583
  });
579
584
  });
580
585
  });
586
+
587
+ /**
588
+ * Set up a temp workspace with a package.json and optional pnpm-workspace.yaml.
589
+ * @param {object} packageJson - package.json contents
590
+ * @param {string} [workspaceYaml] - pnpm-workspace.yaml contents, omitted to skip the file
591
+ * @returns {Promise<string>} The workspace directory
592
+ */
593
+ async function makeWorkspace(packageJson, workspaceYaml) {
594
+ const cwd = await makeTempDir();
595
+ const writes = [writePackageJson(cwd, packageJson)];
596
+ if (workspaceYaml !== undefined) {
597
+ writes.push(fs.writeFile(path.join(cwd, 'pnpm-workspace.yaml'), workspaceYaml));
598
+ }
599
+ await Promise.all(writes);
600
+ return cwd;
601
+ }
602
+
603
+ /**
604
+ * @param {string} cwd
605
+ * @returns {Promise<string>}
606
+ */
607
+ function readWorkspaceYaml(cwd) {
608
+ return fs.readFile(path.join(cwd, 'pnpm-workspace.yaml'), 'utf8');
609
+ }
610
+
611
+ describe('writeOverridesToWorkspace', () => {
612
+ describe('writing to pnpm-workspace.yaml', () => {
613
+ it('creates the file with an overrides block when none exists', async () => {
614
+ const cwd = await makeWorkspace({ name: 'root' });
615
+
616
+ await writeOverridesToWorkspace(cwd, { foo: '1.2.3' });
617
+
618
+ expect(await readWorkspaceYaml(cwd)).toMatchInlineSnapshot(`
619
+ "overrides:
620
+ foo: 1.2.3
621
+ "
622
+ `);
623
+ });
624
+
625
+ it('merges into an existing block, preserving comments and quoting scoped names', async () => {
626
+ const cwd = await makeWorkspace(
627
+ { name: 'root' },
628
+ [
629
+ 'packages:',
630
+ " - 'packages/*'",
631
+ 'overrides:',
632
+ ' # keep this pin',
633
+ " bar: '2.0.0'",
634
+ '',
635
+ ].join('\n'),
636
+ );
637
+
638
+ await writeOverridesToWorkspace(cwd, { playwright: '1.49.1', '@playwright/test': '1.49.1' });
639
+
640
+ expect(await readWorkspaceYaml(cwd)).toMatchInlineSnapshot(`
641
+ "packages:
642
+ - 'packages/*'
643
+ overrides:
644
+ # keep this pin
645
+ bar: '2.0.0'
646
+ playwright: 1.49.1
647
+ "@playwright/test": 1.49.1
648
+ "
649
+ `);
650
+ // The package.json is left untouched.
651
+ expect(await readPackageJson(cwd)).toEqual({ name: 'root' });
652
+ });
653
+
654
+ it('overwrites a same-named override, keeping its quote style', async () => {
655
+ const cwd = await makeWorkspace(
656
+ { name: 'root' },
657
+ ['overrides:', " foo: '1.0.0'", ''].join('\n'),
658
+ );
659
+
660
+ await writeOverridesToWorkspace(cwd, { foo: '2.0.0' });
661
+
662
+ expect(await readWorkspaceYaml(cwd)).toMatchInlineSnapshot(`
663
+ "overrides:
664
+ foo: '2.0.0'
665
+ "
666
+ `);
667
+ });
668
+
669
+ it('prefers the workspace file when both manifests define overrides', async () => {
670
+ const cwd = await makeWorkspace(
671
+ { name: 'root', pnpm: { overrides: { baz: '3.0.0' } } },
672
+ ['overrides:', " bar: '2.0.0'", ''].join('\n'),
673
+ );
674
+
675
+ await writeOverridesToWorkspace(cwd, { foo: '1.2.3' });
676
+
677
+ expect(await readWorkspaceYaml(cwd)).toContain('foo: 1.2.3');
678
+ // package.json overrides are left where they were.
679
+ expect(await readPackageJson(cwd)).toEqual({
680
+ name: 'root',
681
+ pnpm: { overrides: { baz: '3.0.0' } },
682
+ });
683
+ });
684
+ });
685
+
686
+ describe('writing to package.json', () => {
687
+ it('honors the package.json location when no workspace overrides exist', async () => {
688
+ const cwd = await makeWorkspace({
689
+ name: 'root',
690
+ pnpm: { overrides: { foo: '1.0.0' }, packageExtensions: { thing: {} } },
691
+ });
692
+
693
+ await writeOverridesToWorkspace(cwd, { bar: '2.0.0' });
694
+
695
+ expect(await readPackageJson(cwd)).toEqual({
696
+ name: 'root',
697
+ pnpm: { overrides: { foo: '1.0.0', bar: '2.0.0' }, packageExtensions: { thing: {} } },
698
+ });
699
+ // No workspace file is created.
700
+ await expect(fs.access(path.join(cwd, 'pnpm-workspace.yaml'))).rejects.toThrow();
701
+ });
702
+
703
+ it('lets computed overrides win over an existing package.json override', async () => {
704
+ const cwd = await makeWorkspace({ name: 'root', pnpm: { overrides: { foo: '1.0.0' } } });
705
+
706
+ await writeOverridesToWorkspace(cwd, { foo: '2.0.0' });
707
+
708
+ expect(await readPackageJson(cwd)).toEqual({
709
+ name: 'root',
710
+ pnpm: { overrides: { foo: '2.0.0' } },
711
+ });
712
+ });
713
+ });
714
+
715
+ describe('rejecting resolutions', () => {
716
+ it('throws and writes nothing when package.json has a resolutions field', async () => {
717
+ const cwd = await makeWorkspace({ name: 'root', resolutions: { foo: '1.0.0' } });
718
+
719
+ await expect(writeOverridesToWorkspace(cwd, { bar: '2.0.0' })).rejects.toThrow(/resolutions/);
720
+ await expect(fs.access(path.join(cwd, 'pnpm-workspace.yaml'))).rejects.toThrow();
721
+ });
722
+
723
+ it('ignores an empty resolutions field', async () => {
724
+ const cwd = await makeWorkspace({ name: 'root', resolutions: {} });
725
+
726
+ await writeOverridesToWorkspace(cwd, { foo: '1.2.3' });
727
+
728
+ expect(await readWorkspaceYaml(cwd)).toContain('foo: 1.2.3');
729
+ });
730
+ });
731
+ });