Skip to content

Conversation

brijeshb42
Copy link
Contributor

@brijeshb42 brijeshb42 commented Sep 4, 2025

This exposes a cli command extract-error-codes to extract annotated errors from workspace packages. This makes the error code extraction independent of the build process. The command is supposed to work on the whole workspace instead of at the individual package level (unless we have a use case later).
There's a way to make this even faster by just processing the files that have changed in this branch instead of all the files in all the packages. But the current gains are already huge and using git would be a micro-optimization. We could revisit this in future.

Also created a helper wrapInWorker function to abstract creation of worker pools and migrated code to use it.

Closes #580

Repo PRs -

  1. [code-infra] Make error code extraction independent of build material-ui#46865
  2. [code-infra] Migrate error code extraction to code-infra base-ui#2670

@brijeshb42 brijeshb42 added the scope: code-infra Involves the code-infra product (https://www.notion.so/mui-org/5562c14178aa42af97bc1fa5114000cd). label Sep 4, 2025
@mui-bot
Copy link

mui-bot commented Sep 4, 2025

Bundle size report

Bundle Parsed size Gzip size
@base-ui-components/react 0B(0.00%) 0B(0.00%)

Details of bundle changes

@oliviertassinari oliviertassinari changed the title [infra] Add cli to extract error codes independent of build [code-infra] Add cli to extract error codes independent of build Sep 6, 2025
@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged. label Sep 7, 2025
@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged. label Sep 8, 2025
* @param {'all' | 'allSettled'} [options.promiseMethod='all']
* @returns {Promise<void>}
*/
export async function wrapInWorker(fn, options) {
Copy link
Member

@Janpot Janpot Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion to simplify this helper a bit:

async function mapConcurrently<T, R>(items: T[], mapper: (t: T) => Promise<R>, concurrency: number): Promise<R[]>
  • Make the signature more like a map operation, allow returning results, to satisfy more use-cases.
  • Remove the promiseMethod, this can just be done in the mapper by the consumer. We can avoid leaking the implementation this way.
  • Make concurrency explicitly required, avoid consumers to rely on a arbitrary default.

workers.push(worker);
}

await Promise.allSettled(workers);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like allSettled wasn't wanted here, the JSON.parse is already wrapped, and the fs.readFile we would want to propagate its errors.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thinking was that we'd want to know about all the erroneous json files in the same run instead of just failing on the first wrong file.
This way, devs can fix the files together instead of running the command multiple times.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would still work, the JSON.parse is wrapped in try/catch already, and the catch just logs. as far as I can see, the allSettled is not doing anything, except from hiding errors from fs.readFile, which is undesired.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. I figured later. Removed the option.

* @param {Function} fn - The function to measure.
* @returns {Promise<number>} - The duration of the function execution in milliseconds.
*/
export async function measurePerf(label, fn) {
Copy link
Member

@Janpot Janpot Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Poor man's tracer 😄 . Suggestion: could be more versatile if we maintain return value of the function, and instead

async function markFn<F extends Function>(label: string, fn: (...args: Parameters<F>)): Awaitable<ReturnValue<F>>;
// and
async function measureFn(label: string)

// then use as

const result = await markFn('foo', async () => {
  return // ...
});

console.log(`It took ${measureFn('foo').duration}ms to calculate ${result}`)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling markFn('foo',...) and then again measureFn('foo') seems repititive. Should be another way to do both in the same call.

Copy link
Member

@Janpot Janpot Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's sort of repetitive, but ideally we make it so we can wrap existing code with measureFn, including code that returns values. I think it would be nice to just simply complement the performance.mark and performance.measure functions with functions that can automatically wrap and keep the same API philosophy of being able to just insert some function calls without semantically having to change the program.

Doesn't matter much, not a blocker for this PR at all

return shouldNotSkip;
},
);
await Promise.all(packages.map((pkg) => extractErrorCodesForPackage(pkg, errors, detection)));
Copy link
Member

@Janpot Janpot Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to determine the full list of files across all packages, and then run the extractor concurrently over that? Otherwise we multiply the concurrency with each specified package. i.e. 1 package => concurrency 30, 3 packages => concurrency becomes 90. So we then would have concurrency of 90, but it wouldn't be fully used all the time if there's e.g. an asymmetrically large package in there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. That's better. I was working on the pretext of packages but your suggestion makes sense.

Copy link
Member

@Janpot Janpot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love this, made a few suggestions, but no blockers for me.

@oliviertassinari oliviertassinari temporarily deployed to extract-error-codes - mui-tools-public PR #641 September 8, 2025 10:26 — with Render Destroyed
@brijeshb42 brijeshb42 requested a review from Janpot September 8, 2025 10:32
@brijeshb42 brijeshb42 enabled auto-merge (squash) September 8, 2025 10:35
Copy link
Member

@Janpot Janpot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👌

@brijeshb42 brijeshb42 merged commit a120f1e into master Sep 8, 2025
9 checks passed
@brijeshb42 brijeshb42 deleted the extract-error-codes branch September 8, 2025 10:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
performance scope: code-infra Involves the code-infra product (https://www.notion.so/mui-org/5562c14178aa42af97bc1fa5114000cd).
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[code-infra] Port error minification
4 participants