Skip to content

Commit 30e7ab7

Browse files
committed
Review fixes
1 parent edbabee commit 30e7ab7

File tree

6 files changed

+100
-73
lines changed

6 files changed

+100
-73
lines changed

packages/code-infra/src/cli/cmdCopyFiles.mjs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { findWorkspaceDir } from '@pnpm/find-workspace-dir';
22
import { globby } from 'globby';
33
import fs from 'node:fs/promises';
44
import path from 'node:path';
5-
import { wrapInWorker } from '../utils/build.mjs';
5+
import { mapConcurrently } from '../utils/build.mjs';
66

77
/**
88
* @typedef {Object} Args
@@ -106,11 +106,12 @@ async function processGlobs({ globs, cwd, silent = true, buildDir }) {
106106
});
107107
});
108108

109-
await wrapInWorker(
109+
await mapConcurrently(
110+
filesToProcess,
110111
async (file) => {
111112
await recursiveCopy({ source: file.sourcePath, target: file.targetPath, silent });
112113
},
113-
{ items: filesToProcess },
114+
50,
114115
);
115116
return filesToProcess.length;
116117
}

packages/code-infra/src/cli/cmdExtractErrorCodes.mjs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-disable no-console */
22

3-
import { measurePerf } from '../utils/build.mjs';
3+
import { markFn, measureFn } from '../utils/build.mjs';
44

55
/**
66
* @typedef {import('../utils/extractErrorCodes.mjs').Args} Args
@@ -29,11 +29,13 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
2929
});
3030
},
3131
async handler(args) {
32-
console.log(`🔨 Extracting error codes`);
33-
const duration = await measurePerf(/** @type {string} */ (args._[0]), async () => {
32+
const commandName = /** @type {string} */ (args._[0]);
33+
await markFn(commandName, async () => {
3434
const module = await import('../utils/extractErrorCodes.mjs');
3535
await module.default(args);
3636
});
37-
console.log(`✅ Extracted error codes in ${(duration / 1000.0).toFixed(3)}s`);
37+
console.log(
38+
`✅ Extracted error codes in ${(measureFn(commandName).duration / 1000.0).toFixed(3)}s`,
39+
);
3840
},
3941
});

packages/code-infra/src/cli/cmdJsonLint.mjs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import chalk from 'chalk';
44
import fs from 'node:fs/promises';
55
import { globby } from 'globby';
66
import path from 'node:path';
7-
import { wrapInWorker } from '../utils/build.mjs';
7+
import { mapConcurrently } from '../utils/build.mjs';
88

99
/**
1010
* @typedef {Object} Args
@@ -45,7 +45,8 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
4545

4646
let passed = true;
4747

48-
await wrapInWorker(
48+
await mapConcurrently(
49+
filenames,
4950
async (filename) => {
5051
const content = await fs.readFile(path.join(cwd, filename), { encoding: 'utf8' });
5152
try {
@@ -59,7 +60,7 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
5960
console.error(failMessage(`Error parsing ${filename}:\n\n${String(error)}`));
6061
}
6162
},
62-
{ items: filenames, defaultConcurrency: 20, promiseMethod: 'allSettled' },
63+
20,
6364
);
6465
if (!passed) {
6566
throw new Error('❌ At least one file did not pass. Check the console output');

packages/code-infra/src/utils/build.mjs

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -70,17 +70,31 @@ export function validatePkgJson(packageJson, options = {}) {
7070
}
7171

7272
/**
73-
* Measures the performance of a function.
74-
* @param {string} label - The label for the performance measurement.
75-
* @param {Function} fn - The function to measure.
76-
* @returns {Promise<number>} - The duration of the function execution in milliseconds.
73+
* Marks the start and end of a function execution for performance measurement.
74+
* Uses the Performance API to create marks and measure the duration.
75+
* @function
76+
* @template {() => Promise<any>} F
77+
* @param {string} label
78+
* @param {() => ReturnType<F>} fn
79+
* @returns {Promise<ReturnType<F>>}
80+
*/
81+
export async function markFn(label, fn) {
82+
const startMark = `${label}-start`;
83+
const endMark = `${label}-end`;
84+
performance.mark(startMark);
85+
const result = await fn();
86+
performance.mark(endMark);
87+
performance.measure(label, startMark, endMark);
88+
return result;
89+
}
90+
91+
/**
92+
* @param {string} label
7793
*/
78-
export async function measurePerf(label, fn) {
79-
performance.mark(`${label}-start`);
80-
await Promise.resolve(fn());
81-
performance.mark(`${label}-end`);
82-
const measurement = performance.measure(label, `${label}-start`, `${label}-end`);
83-
return measurement.duration;
94+
export function measureFn(label) {
95+
const startMark = `${label}-start`;
96+
const endMark = `${label}-end`;
97+
return performance.measure(label, startMark, endMark);
8498
}
8599

86100
export const BASE_IGNORES = [
@@ -96,35 +110,40 @@ export const BASE_IGNORES = [
96110
];
97111

98112
/**
99-
* A utility to wrap a function in a worker pool.
113+
* A utility to map a function over an array of items in a worker pool.
100114
*
101115
* This function will create a pool of workers and distribute the items to be processed among them.
102116
* Each worker will process items sequentially, but multiple workers will run in parallel.
117+
*
103118
* @function
104119
* @template T
105-
* @param {(item: T) => Promise<void>} fn
106-
* @param {Object} options
107-
* @param {T[]} options.items
108-
* @param {number} [options.defaultConcurrency=50]
109-
* @param {'all' | 'allSettled'} [options.promiseMethod='all']
110-
* @returns {Promise<void>}
120+
* @template R
121+
* @param {T[]} items
122+
* @param {(item: T) => Promise<R>} mapper
123+
* @param {number} concurrency
124+
* @returns {Promise<(R|Error)[]>}
111125
*/
112-
export async function wrapInWorker(fn, options) {
113-
const { defaultConcurrency = 50, items = [], promiseMethod = 'all' } = options ?? {};
114-
if (items.length === 0) {
115-
return;
126+
export async function mapConcurrently(items, mapper, concurrency) {
127+
if (!items.length) {
128+
return Promise.resolve([]); // nothing to do
116129
}
117-
const itemIterator = items[Symbol.iterator]();
118-
const concurrency = Math.min(defaultConcurrency, items.length);
130+
const itemIterator = items.entries();
131+
const count = Math.min(concurrency, items.length);
119132
const workers = [];
120-
for (let i = 0; i < concurrency; i += 1) {
133+
/**
134+
* @type {(R|Error)[]}
135+
*/
136+
const results = new Array(items.length);
137+
for (let i = 0; i < count; i += 1) {
121138
const worker = Promise.resolve().then(async () => {
122-
for (const item of itemIterator) {
139+
for (const [index, item] of itemIterator) {
123140
// eslint-disable-next-line no-await-in-loop
124-
await fn(item);
141+
const res = await mapper(item);
142+
results[index] = res;
125143
}
126144
});
127145
workers.push(worker);
128146
}
129-
await (promiseMethod === 'all' ? Promise.all(workers) : Promise.allSettled(workers));
147+
await Promise.all(workers);
148+
return results;
130149
}

packages/code-infra/src/utils/extractErrorCodes.mjs

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import * as fs from 'node:fs/promises';
88
import * as path from 'node:path';
99

1010
import { getWorkspacePackages } from '../cli/pnpm.mjs';
11-
import { BASE_IGNORES, wrapInWorker } from './build.mjs';
11+
import { BASE_IGNORES, mapConcurrently } from './build.mjs';
1212

1313
/**
1414
* @typedef {Object} Args
@@ -18,12 +18,12 @@ import { BASE_IGNORES, wrapInWorker } from './build.mjs';
1818
*/
1919

2020
/**
21-
* Extracts error codes from all files in a directory.
21+
* Gets all relevant files for a package to parse.
22+
*
2223
* @param {import('../cli/pnpm.mjs').PublicPackage} pkg
23-
* @param {Set<string>} errors
24-
* @param {import('@mui/internal-babel-plugin-minify-errors').Options['detection']} [detection='opt-in']
24+
* @returns {Promise<string[]>} An array of file paths.
2525
*/
26-
async function extractErrorCodesForPackage(pkg, errors, detection = 'opt-in') {
26+
async function getFilesForPackage(pkg) {
2727
const srcPath = path.join(pkg.path, 'src');
2828
const srcPathExists = await fs
2929
.stat(srcPath)
@@ -42,11 +42,19 @@ async function extractErrorCodesForPackage(pkg, errors, detection = 'opt-in') {
4242
],
4343
cwd,
4444
});
45-
const workerCount = Math.min(40, files.length);
46-
console.log(`🔍 ${pkg.name}: Processing ${files.length} file${files.length > 1 ? 's' : ''}...`);
47-
await wrapInWorker(
48-
async (file) => {
49-
const fullPath = path.join(cwd, file);
45+
return files.map((file) => path.join(cwd, file));
46+
}
47+
48+
/**
49+
* Extracts error codes from all files in a directory.
50+
* @param {string[]} files
51+
* @param {Set<string>} errors
52+
* @param {import('@mui/internal-babel-plugin-minify-errors').Options['detection']} [detection='opt-in']
53+
*/
54+
async function extractErrorCodesForWorkspace(files, errors, detection = 'opt-in') {
55+
await mapConcurrently(
56+
files,
57+
async (fullPath) => {
5058
const code = await fs.readFile(fullPath, 'utf8');
5159
const ast = await parseAsync(code, {
5260
filename: fullPath,
@@ -73,7 +81,7 @@ async function extractErrorCodesForPackage(pkg, errors, detection = 'opt-in') {
7381
},
7482
});
7583
},
76-
{ defaultConcurrency: workerCount, items: files },
84+
30,
7785
);
7886
}
7987

@@ -86,6 +94,9 @@ export default async function extractErrorCodes(args) {
8694
* @type {Set<string>}
8795
*/
8896
const errors = new Set();
97+
98+
// Find relevant files
99+
89100
const basePackages = await getWorkspacePackages({
90101
publicOnly: true,
91102
});
@@ -97,7 +108,19 @@ export default async function extractErrorCodes(args) {
97108
!pkg.name.startsWith('@mui-internal/') &&
98109
!skipPackages.includes(pkg.name),
99110
);
100-
await Promise.all(packages.map((pkg) => extractErrorCodesForPackage(pkg, errors, detection)));
111+
const files = await Promise.all(packages.map((pkg) => getFilesForPackage(pkg)));
112+
packages.forEach((pkg, index) => {
113+
console.log(
114+
`🔍 ${pkg.name}: Found ${files[index].length} file${files[index].length > 1 ? 's' : ''}`,
115+
);
116+
});
117+
118+
// Extract error codes from said files.
119+
const filesToProcess = files.flat();
120+
console.log(`🔍 Extracting error codes from ${filesToProcess.length} files...`);
121+
await extractErrorCodesForWorkspace(filesToProcess, errors, detection);
122+
123+
// Write error codes to the specified file.
101124
const errorCodeFilePath = path.resolve(errorCodesPath);
102125
const fileExists = await fs
103126
.stat(errorCodeFilePath)
@@ -133,13 +156,13 @@ export default async function extractErrorCodes(args) {
133156
if (!fileExists) {
134157
await fs.mkdir(path.dirname(errorCodeFilePath), { recursive: true });
135158
}
136-
await fs.writeFile(errorCodeFilePath, `${JSON.stringify(finalErrorCodes, null, 2)}\n`);
137159
const newErrorCount = inverseLookupCode.size - originalErrorCount;
138160
if (newErrorCount === 0) {
139161
console.log(`✅ No new error codes found.`);
140162
} else {
141163
console.log(
142164
`📝 Wrote ${newErrorCount} new error code${newErrorCount > 1 ? 's' : ''} to "${errorCodesPath}"`,
143165
);
166+
await fs.writeFile(errorCodeFilePath, `${JSON.stringify(finalErrorCodes, null, 2)}\n`);
144167
}
145168
}

0 commit comments

Comments
 (0)