Skip to content

Conversation

JCQuintas
Copy link
Member

@JCQuintas JCQuintas commented Jul 25, 2025

This PR tries to fix asymmetric exports in community and pro+ packages

Eg: If you are using a PieChart on community, you would use it like this

import { PieChart } from '@mui/x-charts/PieChart';

But then on pro, you import it from the root, because the is no src/PieChart folder in the pro package.

import { PieChart } from '@mui/x-charts-pro';

This change is a naive implementation that allows users to deep import, as

import { PieChart } from '@mui/x-charts-pro/PieChart';

@JCQuintas JCQuintas requested a review from a team July 25, 2025 20:39
@JCQuintas JCQuintas self-assigned this Jul 25, 2025
@JCQuintas JCQuintas added type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature. scope: code-infra Changes related to the core-infra product. labels Jul 25, 2025
@mui-bot
Copy link

mui-bot commented Jul 25, 2025

Localization writing tips ✍️

Seems you are updating localization 🌍 files.

Thank you for contributing to the localization! 🎉 To make your PR perfect, here is a list of elements to check: ✔️

  • Verify if the PR title respects the release format. Here are two examples (depending if you update or add a locale file)

    [l10n] Improve Swedish (sv-SE) locale
    [l10n] Add Danish (da-DK) locale

  • Update the documentation of supported locales by running pnpm l10n
  • Verify that you have added an export line in src/locales/index.ts for the new locale.
  • Run pnpm docs:api which should add your new translation to the list of exported interfaces.
  • Clean files with pnpm prettier.

Deploy preview: https://deploy-preview-18917--material-ui-x.netlify.app/

Bundle size report

Bundle Parsed Size Gzip Size
@mui/x-data-grid 0B(0.00%) 0B(0.00%)
@mui/x-data-grid/DataGrid 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-pro 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-pro/DataGridPro 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-premium 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-premium/DataGridPremium 0B(0.00%) 0B(0.00%)
@mui/x-charts 0B(0.00%) 0B(0.00%)
@mui/x-charts-pro ▼-471B(-0.12%) 0B(0.00%)
@mui/x-date-pickers 0B(0.00%) 0B(0.00%)
@mui/x-date-pickers-pro 0B(0.00%) 0B(0.00%)
@mui/x-tree-view 0B(0.00%) 0B(0.00%)
@mui/x-tree-view-pro 0B(0.00%) 0B(0.00%)

Details of bundle changes

Generated by 🚫 dangerJS against e707d4b

@JCQuintas JCQuintas marked this pull request as ready for review July 25, 2025 20:44
Copy link
Member

@bernardobelchior bernardobelchior left a comment

Choose a reason for hiding this comment

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

Great initiative 👍

const utilityDirs = [
'internals',
'context',
'hooks',
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't this be configured by package?

Also, shouldn't we remove hooks from this?

For example, the SeriesDemo uses deep imports from the hooks directory: import { useLineSeries, useXAxis, useXScale, useYScale } from '@mui/x-charts/hooks';

Copy link
Member Author

Choose a reason for hiding this comment

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

🤔 I assumed we were already exporting hooks from pro/hooks/index.ts but we are not. However the file exists, so I would rather we just add it there.

This is a naive approach, so it still requires us to do some work to ensure everything is correct

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, this is already a step forward.

I wonder if we should have a auto generated section in a file instead.

We can use something like:

// User code here

// # region auto-generated

// Generated code here

// # endregion

So we can gradually move the hooks export to auto generated ones.

Something for the future, though.

Copy link
Member Author

Choose a reason for hiding this comment

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

We could, but it also is a good recipe to create conflicts 🫠

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll try to do it for specific folders instead. Like context/hooks

Copy link
Member Author

Choose a reason for hiding this comment

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

I've made it configurable and allow block comment in specific utilities packages.

Copy link
Member

Choose a reason for hiding this comment

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

but it also is a good recipe to create conflicts 🫠

Agree.

TLTR; Instead of having this feature, we could add the missing export * from ... manually and remove this notion of utilities. We would end up in the same state.


I don't see why utils should be treated differently from components. For me there are two categories

  • stuff users need to import, and we should be consistent
  • stuff users don't care about, and we can ignore them.

Imagine you add a new utils folder.

Step 1: pro and premium have nothing to add. The autogenerated index.ts can be the same as the one for component
Step 2: pro needs to add an extra function. We remove the comments but keep the export * from .... Now the file is ignored by the script and we are responsible for it. But not removing an export * from ... shoudl be doable :)

Copy link
Member Author

Choose a reason for hiding this comment

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

I think now it is implemented we might as well have it, since I added other functionalities to the utilities as well 😢

@@ -57,6 +57,7 @@
"release:publish:dry-run": "pnpm publish --recursive --tag latest --registry=\"http://localhost:4873/\"",
"release:tag": "node scripts/releaseTag.mjs",
"release:prepare": "node scripts/createReleasePR.mjs",
"generate:exports": "node scripts/generateDeepExports.mjs",
Copy link
Contributor

@brijeshb42 brijeshb42 Jul 28, 2025

Choose a reason for hiding this comment

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

Instead of checking in the CI, why not call this script from the build command directly? This will avoid requiring users to run the script before committing.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm just following our current approach of committing the generated stuff rather than having it only on build-time.

If @mui/infra wants to go that way I could happily switch 😆

Copy link
Member Author

Choose a reason for hiding this comment

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

It would probably not work too well if we only generate on build-time though 🤔, as the imports wouldn't be available in, lets say, the docs pages... 😢

Copy link
Member

@Janpot Janpot Jul 28, 2025

Choose a reason for hiding this comment

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

IMO the approach in this PR is better. It's easier to gain more control over it when it's part of source code, and it makes the build less magic.


return (
fs
.readdirSync(srcPath, { withFileTypes: true })
Copy link
Member

@Janpot Janpot Jul 28, 2025

Choose a reason for hiding this comment

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

As a general rule, I would prefer it if we stopped using blocking io everywhere.

Copy link
Member Author

Choose a reason for hiding this comment

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

I tried it out, but I think it makes sense for this instance to use sync funcs. Since the behaviour depends on changes made by this script, eg x-charts-premium needs to know the changes done to x-charts-pro, so we have to do one package after another.

We could have async read/write inside a given package, but the async code is much more verbose for this specific implementation 😓

This script runs super fast anyways, so it might not be needed to optimise

Copy link
Member

Choose a reason for hiding this comment

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

We can keep processing the packages sequentially, but so much file access can run parallel in this PR. It's hardly more verbose with async/await if done properly. Most of the calls here can just have their non-blocking counterpart with an await. just the few loops have to be a replaced wit a Promise.all.

Copy link
Member

@alexfauquette alexfauquette left a comment

Choose a reason for hiding this comment

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

Nice one

Comment on lines +1 to +3
// This re-export-block is automatically generated, to customize, simply remove the block.
export * from '@mui/x-charts/colorPalettes';
// End of re-export-block
Copy link
Member

Choose a reason for hiding this comment

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

Why is this a block comment and not just

// Re-export automatically generated, to customize, simply remove this line.

like for previouse ones?

Copy link
Member

Choose a reason for hiding this comment

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

I finally got it. It's because the folder is part of the utilities

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, I wanted to have a full file override mode and a partial one. The idea is that some files are more often customised, like hooks, while others are unlikely to be reimplemented, like BarChart

const utilityDirs = [
'internals',
'context',
'hooks',
Copy link
Member

Choose a reason for hiding this comment

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

but it also is a good recipe to create conflicts 🫠

Agree.

TLTR; Instead of having this feature, we could add the missing export * from ... manually and remove this notion of utilities. We would end up in the same state.


I don't see why utils should be treated differently from components. For me there are two categories

  • stuff users need to import, and we should be consistent
  • stuff users don't care about, and we can ignore them.

Imagine you add a new utils folder.

Step 1: pro and premium have nothing to add. The autogenerated index.ts can be the same as the one for component
Step 2: pro needs to add an extra function. We remove the comments but keep the export * from .... Now the file is ignored by the script and we are responsible for it. But not removing an export * from ... shoudl be doable :)

JCQuintas and others added 3 commits July 29, 2025 10:49
Co-authored-by: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com>
Signed-off-by: Jose C Quintas Jr <juniorquintas@gmail.com>
Copy link
Member

@bernardobelchior bernardobelchior left a comment

Choose a reason for hiding this comment

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

Not sure why the new GitHub UI doesn't let me just leave comments, I need to write this comment before it lets me comment 😅

Comment on lines 176 to 180
if (!existsSync(srcPath)) {
return [];
}

const dirents = await fs.readdir(srcPath, { withFileTypes: true });
Copy link
Member

@bernardobelchior bernardobelchior Jul 30, 2025

Choose a reason for hiding this comment

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

Would it be better to readdir and handle the ENOENT error if the directory doesn't exist?

It's one less call. For a lot of files it might be faster.

Same applies on line 189, and other lines across this script.

Not a blocker, though. Feel free to do it as a follow-up if you prefer.

Copy link
Member

@Janpot Janpot Jul 30, 2025

Choose a reason for hiding this comment

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

Plus it's vulnerable to race conditions. Another program may remove the dir in between the exists check and the readdir call. So you technically have to handle that error anyway. At which point the exists check becomes unnecessary. I believe this would be one of the reasons why exists was deprecated in the first place. It was mostly used in patterns that are vulnerable to race conditions.

Copy link
Member

Choose a reason for hiding this comment

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

Another program may remove the dir in between the exists check and the readdir call

Yeah, I didn't add that because I think it's unlikely to happen in this case, but it's true it's a possibility

Copy link
Member Author

Choose a reason for hiding this comment

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

It's one less call. For a lot of files it might be faster.

The script is quite fast, I don't think there is any concern in that aspect. I've refactored to use catch blocks instead on existsSync however.

Copy link
Member

Choose a reason for hiding this comment

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

nitpick, but those catch blocks could potentially hide other errors than just the missing dir.

}

// Get additional components specific to pro/premium
// eslint-disable-next-line no-await-in-loop
Copy link
Member

Choose a reason for hiding this comment

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

Should we follow this lint? It'd probably make the script slightly faster by processing files concurrently.

Also not a blocker, as it doesn't affect the functionality of the script.

Copy link
Member Author

Choose a reason for hiding this comment

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

No, these need to run in order

Copy link
Member

@bernardobelchior bernardobelchior Jul 30, 2025

Choose a reason for hiding this comment

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

Ah, you mean because the premium package depends on the generated exports of the pro package?

Yeah, makes sense. We could have some concurrency here, but probably not worth the extra complexity.

@@ -204,7 +207,13 @@
{ "name": "DefaultizedSeriesType", "kind": "TypeAlias" },
{ "name": "defaultOnBeforeExport", "kind": "Function" },
{ "name": "Direction", "kind": "TypeAlias" },
{ "name": "elGR", "kind": "Variable" },
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't we avoid exporting the locales from the index file?

When someone is doing import { LineChartPro } from '@mui/x-charts-pro', I believe these locales will still need to be evaluated by the bundler, even if they're dropped in the end. @Janpot can probably confirm here.

We aren't exporting the locales from the community package.

Copy link
Member

@Janpot Janpot Jul 30, 2025

Choose a reason for hiding this comment

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

Some bundlers like vite would have some overhead in dev mode with barrel files. Next.js shouldn't be impacted as they have built special handling of these barrel files. IMO, until vite fixes that issue, we should just advice performance-conscious users to not user the barrel file.

Copy link
Member Author

Choose a reason for hiding this comment

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

I've reverted it to be in line with our current approach

Copy link
Member Author

Choose a reason for hiding this comment

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

Left comments in the index files as well

Comment on lines +48 to +49
// Locales should be imported from `@mui/x-charts-premium/locales`
// export * from './locales';
Copy link
Member

Choose a reason for hiding this comment

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

Should we remove this? Why keep it commented?

Copy link
Member Author

Choose a reason for hiding this comment

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

So we don't make the same mistake again

Copy link
Member

Choose a reason for hiding this comment

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

Not sure if it'll be very effective, but I'm ok with it

@@ -1,12 +1,9 @@
// TODO: Add typeOverloads when available
// import './typeOverloads/modules';
// eslint-disable-next-line no-restricted-imports
import '@mui/x-charts-pro/typeOverloads/modules';
Copy link
Member

Choose a reason for hiding this comment

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

Should the type overloads from the premium package import the pro ones instead so that if someone imports @mui/x-charts-premium/typeOverloads, they get the pro ones as well?

Otherwise, they'd need to import the pro and premium type overloads in sequence.

Copy link
Member Author

Choose a reason for hiding this comment

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

Currently there is no premium typeOverloads. But yes, I've left the TODO: there to do that eventually

@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged. label Jul 30, 2025
Copy link

This pull request has conflicts, please resolve those before we can evaluate the pull request.

Signed-off-by: Jose C Quintas Jr <juniorquintas@gmail.com>
@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged. label Jul 30, 2025
Copy link
Member

@bernardobelchior bernardobelchior left a comment

Choose a reason for hiding this comment

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

Great job 💯

@JCQuintas JCQuintas enabled auto-merge (squash) July 30, 2025 17:04
@JCQuintas JCQuintas merged commit 289e9fa into mui:master Jul 30, 2025
20 checks passed
@JCQuintas JCQuintas deleted the deep-generation branch July 30, 2025 17:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
scope: code-infra Changes related to the core-infra product. type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants