Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't pull unused @mui modules by using path imports #168

Closed

Conversation

anthonyalayo
Copy link
Contributor

@anthonyalayo anthonyalayo commented Jan 15, 2023

Problem

Webpack only tree shakes during minification, so this package is pulling in everything from @mui when developing. This results in a very large amount of modules and causes a significant slowdown for development.

This is a known problem, and upon browsing the material-ui github, I could see they are importing the same way.

Attempting to fix this downstream by applying production settings in development doesn't seem to work, it just hangs.
On top of that, the attempted fix #169 doesn't reduce the bundle size either. It transforms the import statements but ends up pulling in other imports from those imports. This solution inlines the @mui dependencies so that the package is minimized in development.

Resources

Solution

Use path imports to only pull in necessary dependencies from @mui.

Tested with a next.js boilerplate using version 13.1.2.

Ran npm link inside @textea/json-viewer
Ran npm link @textea/json-viewer inside the dependent project

Compile time output:

event - compiled client and server successfully in 9.3s (1173 modules)

Next.js module trace output:
image

Previous size:

event - compiled client and server successfully in 17.2s (1936 modules)

Explored Alternatives

#169

@netlify
Copy link

netlify bot commented Jan 15, 2023

Deploy Preview for any-viewer failed.

Name Link
🔨 Latest commit da029e9
🔍 Latest deploy log https://app.netlify.com/sites/any-viewer/deploys/63c951caf788bb00089ee0f9

@codecov
Copy link

codecov bot commented Jan 15, 2023

Codecov Report

Base: 87.40% // Head: 87.40% // No change to project coverage 👍

Coverage data is based on head (da029e9) compared to base (7884a29).
Patch coverage: 100.00% of modified lines in pull request are covered.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #168   +/-   ##
=======================================
  Coverage   87.40%   87.40%           
=======================================
  Files          18       18           
  Lines        1969     1969           
  Branches      349      349           
=======================================
  Hits         1721     1721           
  Misses        248      248           
Impacted Files Coverage Δ
src/components/DataKeyPair.tsx 68.68% <100.00%> (ø)
src/components/DataTypes/Function.tsx 100.00% <100.00%> (ø)
src/components/DataTypes/Object.tsx 86.84% <100.00%> (ø)
src/components/DataTypes/createEasyType.tsx 64.55% <100.00%> (ø)
src/components/Icons.tsx 85.10% <100.00%> (ø)
src/components/mui/DataBox.tsx 100.00% <100.00%> (ø)
src/index.tsx 98.78% <100.00%> (-0.01%) ⬇️
src/stores/typeRegistry.tsx 94.73% <100.00%> (ø)

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@pionxzh
Copy link
Collaborator

pionxzh commented Jan 15, 2023

Actually SWC transform-imports plugin is capable of solving our problem.

This is the configuration I have tested

experimental: {
  plugins: config.browser
    ? []
    : [
        [
          '@swc/plugin-transform-imports',
          {
            '@mui/material': {
              transform: '@mui/material/{{member}}'
            }
          }
        ]
      ]
}

@pionxzh
Copy link
Collaborator

pionxzh commented Jan 15, 2023

But, few problems here.
1. We need to external MUI with regex
2. non-browser build will become CJS/MJS (no UMD as we cannot provide the global for them

I just solved them all. Let me make a PR.

@anthonyalayo
Copy link
Contributor Author

@pionxzh exciting! I'll close this one up.

@pionxzh
Copy link
Collaborator

pionxzh commented Jan 15, 2023

Thanks for the research and detailed documentation. Let's go for this 🚀

@anthonyalayo
Copy link
Contributor Author

Explored the issue in detail and filed an RFC for @mui here. I'll make the finishing touches on this.

@anthonyalayo
Copy link
Contributor Author

@pionxzh @rtritto I'm seeing some sort of ESM/CJS interop build issues coming from the docs, any idea what could be causing it? The very first default import statement fails:

SyntaxError: Cannot use import statement outside a module

Seems related:

@rtritto
Copy link
Contributor

rtritto commented Jan 16, 2023

Maybe the error can be resolved converting this project to ESM.
Can this conversion be considered and added to next milestone? @pionxzh

@anthonyalayo
Copy link
Contributor Author

@rtritto @pionxzh created #181 as a first attempt, could you help get it working completely?

@pionxzh
Copy link
Collaborator

pionxzh commented Jan 17, 2023

I thought we already provided ESM output 🤔
Never mind, I will check what happened.

@pionxzh
Copy link
Collaborator

pionxzh commented Jan 17, 2023

should be good now 👍

@anthonyalayo
Copy link
Contributor Author

Nice! Feel free to merge 😁

@anthonyalayo
Copy link
Contributor Author

@pionxzh can we merge?

src/index.tsx Outdated
ThemeProvider
} from '@mui/material'
import Paper from '@mui/material/Paper'
import { createTheme, ThemeProvider } from '@mui/material/styles'
Copy link
Collaborator

Choose a reason for hiding this comment

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

@anthonyalayo is this expected?
maybe I mess it up in the rebasing 🤔

use this instead?

import createTheme from '@mui/material/styles/createTheme'
import ThemeProvider from '@mui/material/styles/ThemeProvider'

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@pionxzh looks like it's still using cjs for some reason?

10:41:43 PM: /opt/build/repo/node_modules/@mui/material/styles/createTheme.js:1
10:41:43 PM: import _extends from "@babel/runtime/helpers/esm/extends";
10:41:43 PM: ^^^^^^
10:41:43 PM: SyntaxError: Cannot use import statement outside a module
10:41:43 PM:     at internalCompileFunction (node:internal/vm:74:18)
10:41:43 PM:     at wrapSafe (node:internal/modules/cjs/loader:1141:20)
10:41:43 PM:     at Module._compile (node:internal/modules/cjs/loader:1182:27)
10:41:43 PM:     at Module._extensions..js (node:internal/modules/cjs/loader:1272:10)
10:41:43 PM:     at Module.load (node:internal/modules/cjs/loader:1081:32)
10:41:43 PM:     at Module._load (node:internal/modules/cjs/loader:922:12)
10:41:43 PM:     at Module.require (node:internal/modules/cjs/loader:1105:19)
10:41:43 PM:     at require (node:internal/modules/cjs/helpers:103:18)
10:41:43 PM:     at 3898 (/opt/build/repo/docs/.next/server/pages/index.js:726:18)

Copy link
Collaborator

Choose a reason for hiding this comment

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

it's quite weird... 🤔 still finding the root cause

@pionxzh
Copy link
Collaborator

pionxzh commented Jan 18, 2023

I think this is more like an issue on Nextra? IDK how to fix it ._.
The bundling of that module being skipped (? so Node.js complains about the import statement

@anthonyalayo
Copy link
Contributor Author

@pionxzh yeah I was really confused too, I thought it had to do with something on the rollup side... that's why I made the other PR to attempt to move away from rolling up modules. I have no idea where that problem is coming from

@anthonyalayo
Copy link
Contributor Author

I think I figured out the bug @pionxzh ,

1. Next.js server side currently builds in CJS, not ESM:
vercel/next.js#25423
vercel/next.js#32239
vercel/next.js#24334

You can make it use ESM, but then it follows the Node.js definition, which requires .mjs files:
https://nodejs.org/api/modules.html#enabling

This isn't setup/supported around the entire ecosystem yet. The "module" and "main" fields in package.json were a proposal that ended up being used as a de facto solution for bundlers until the official solution (.mjs) came out.

2. The module resolution strategy:
https://www.typescriptlang.org/docs/handbook/module-resolution.html#node

@mui has the following in its package.json:

  "main": "./node/index.js",
  "module": "./index.js",

but (this is the part I'm not 100% certain on):

when the Next.js build encounters:

module.exports = require("@mui/material/styles/createTheme");

it resolves that without looking at main or module, and attempts to resolve it using that exact path. If that's the case, that path actually points to the esm location in @mui.

Hence:

10:41:43 PM: /opt/build/repo/node_modules/@mui/material/styles/createTheme.js:1
10:41:43 PM: import _extends from "@babel/runtime/helpers/esm/extends";
10:41:43 PM: ^^^^^^
10:41:43 PM: SyntaxError: Cannot use import statement outside a module
10:41:43 PM:     at internalCompileFunction (node:internal/vm:74:18)
10:41:43 PM:     at wrapSafe (node:internal/modules/cjs/loader:1141:20)
10:41:43 PM:     at Module._compile (node:internal/modules/cjs/loader:1182:27)
10:41:43 PM:     at Module._extensions..js (node:internal/modules/cjs/loader:1272:10)
10:41:43 PM:     at Module.load (node:internal/modules/cjs/loader:1081:32)
10:41:43 PM:     at Module._load (node:internal/modules/cjs/loader:922:12)
10:41:43 PM:     at Module.require (node:internal/modules/cjs/loader:1105:19)
10:41:43 PM:     at require (node:internal/modules/cjs/helpers:103:18)
10:41:43 PM:     at 3898 (/opt/build/repo/docs/.next/server/pages/index.js:726:18)

If that make sense to you (and let me know if you can confirm/deny with a reference the part I wasn't 100% sure on), I think the fix would be:

  1. emit the paths we are presently emitting, for the ESM build
  2. emit the paths prefixed with /node for the CJS build

Hopefully you have an idea of how we can do that with rollup?

@pionxzh
Copy link
Collaborator

pionxzh commented Jan 19, 2023

Wow. This is helpful and detailed.

If we need to do this kind of path rewriting Maybe it's time to bring back #169 ... 🤣

I will look into it and see how later. Thanks.

@anthonyalayo
Copy link
Contributor Author

Agreed, I was thinking the same, we just need exceptions for those 2-3 odd ones out.

@pionxzh
Copy link
Collaborator

pionxzh commented Jan 19, 2023

I think your assumption 1 is correct.
I tried but those workarounds are so weird.
Will see how #169 goes.

@anthonyalayo
Copy link
Contributor Author

Sounds good, closing this one up for that one

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants