Skip to content

Instantly share code, notes, and snippets.

@developit
Last active December 27, 2022 13:59
Show Gist options
  • Save developit/037baaec2a0a1e64e628c84fdd5c1107 to your computer and use it in GitHub Desktop.
Save developit/037baaec2a0a1e64e628c84fdd5c1107 to your computer and use it in GitHub Desktop.

babel-plugin-transform-mui-imports npm

A plugin to make authoring with MUI components efficient, both for humans and bundlers.

Here's why:

✍️ What you write 👏 What your bundler sees
import {
  AppBar,
  List, ListItem, ListItemAction,
  makeStyles
} from "@material-ui/core";
import { Add } from '@material-ui/icons';
import AppBar from "@material-ui/core/esm/AppBar";
import List from "@material-ui/core/esm/List";
import ListItem from "@material-ui/core/esm/ListItem";
import ListItemAction from "@material-ui/core/esm/ListItemAction";
import { makeStyles } from "@material-ui/core/esm/styles";
import Add from "@material-ui/icons/esm/Add";

Installation

  1. Get it from npm:
npm i babel-plugin-transform-mui-imports
  1. Put it in your Babel config:
{ "plugins": ["transform-mui-imports"] }

Try it out on ASTExplorer

module.exports = function ({ types: t }) {
return {
name: "transform-mui-imports",
visitor: {
ImportDeclaration(path, state) {
const source = path.node.source.value;
if (!/(@material-ui\/(core|icons))$/.test(source)) return;
path.replaceWithMultiple(path.get('specifiers').map(s => {
let specificSource = source + (state.opts.esm===false ? '' : '/esm');
let specifier = t.clone(s.node);
// ignore wildcard and default imports that can't be exploded
if (!t.isImportDefaultSpecifier(s) && !t.isImportNamespaceSpecifier(s)) {
const name = s.node.imported.name;
if (name === 'colors') {
// import { colors } from '..' --> import * as colors from '/colors'
specifier = t.importNamespaceSpecifier(specifier.local);
specificSource += '/colors';
}
else if (/^(makeStyles|createStyles|createMuiTheme|responsiveFontSizes|styled|useTheme|withStyles|withTheme|createGenerateClassName|jssPreset|ServerStyleSheets|StylesProvider|MuiThemeProvider|ThemeProvider|easing|duration|hexToRgb|rgbToHex|hslToRgb|decomposeColor|recomposeColor|getContrastRatio|getLuminance|emphasize|fade|darken|lighten)$/.test(name)) {
// core re-exports all of /styles
// import { styled } from '..' --> import { styled } from '/styles'
specifier = t.clone(s.node);
specificSource += '/styles';
}
else {
// import { List } from '..' --> import List from '/esm/List'
specifier = t.importDefaultSpecifier(specifier.local);
specificSource += '/' + name;
}
}
return t.importDeclaration([specifier], t.stringLiteral(specificSource));
}));
}
}
};
}
{
"name": "babel-plugin-transform-mui-imports",
"version": "0.2.0",
"author": "Jason Miller (https://github.com/developit)",
"main": "babel-plugin-transform-mui-imports.js",
"repo": "gist:037baaec2a0",
"homepage": "https://gist.github.com/developit/037baaec2a0a1e64e628c84fdd5c1107",
"scripts": { "prepack": "mv *babel-plugin-transform-mui-imports.md README.md", "postpack": "mv README.md *babel-plugin-transform-mui-imports.md" },
"peerDependencies": {
"@material-ui/core": "^4"
},
"license": "MIT"
}
@developit
Copy link
Author

@cozuya @azz0r - thanks for the reports! I've just updated the gist and published babel-plugin-transform-mui-imports@0.2.0 that fixes both issues.

@BjoernRave
Copy link

tried using it with next.js and got this:

/Users/bjoern/projects/inventhora/frontend/node_modules/@material-ui/core/esm/styles/index.js:1
export * from './colorManipulator';
^^^^^^

SyntaxError: Unexpected token 'export'
    at Module._compile (internal/modules/cjs/loader.js:891:18)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:991:10)
    at Module.load (internal/modules/cjs/loader.js:811:32)
    at Function.Module._load (internal/modules/cjs/loader.js:723:14)
    at Module.require (internal/modules/cjs/loader.js:848:19)
    at require (internal/modules/cjs/helpers.js:74:18)
    at Object.@material-ui/core/esm/styles (/Users/bjoern/projects/inventhora/frontend/.next/server/static/development/pages/_app.js:4186:18)
    at __webpack_require__ (/Users/bjoern/projects/inventhora/frontend/.next/server/static/development/pages/_app.js:23:31)
    at Module../pages/_app.tsx (/Users/bjoern/projects/inventhora/frontend/.next/server/static/development/pages/_app.js:3764:86)
    at __webpack_require__ (/Users/bjoern/projects/inventhora/frontend/.next/server/static/development/pages/_app.js:23:31)
    at Object.0 (/Users/bjoern/projects/inventhora/frontend/.next/server/static/development/pages/_app.js:4031:18)
    at __webpack_require__ (/Users/bjoern/projects/inventhora/frontend/.next/server/static/development/pages/_app.js:23:31)
    at /Users/bjoern/projects/inventhora/frontend/.next/server/static/development/pages/_app.js:91:18
    at Object.<anonymous> (/Users/bjoern/projects/inventhora/frontend/.next/server/static/development/pages/_app.js:94:10)
    at Module._compile (internal/modules/cjs/loader.js:955:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:991:10)

@Cretezy
Copy link

Cretezy commented Jan 24, 2020

This is not needed with Next.js as it already does proper tree-shaking on MUI.

If you really want to you use with Next, you can pass the esm option as false:

{
  "presets": ["next/babel"],
  "plugins": [
    ["babel-plugin-transform-mui-imports", { "esm": false }]
  ]
}

@developit
Copy link
Author

@Cretezy @BjoernRave If you have a way to apply the plugin only to Next.js client bundle builds, it should work. The server build doesn't bundle node_modules, so ES Modules usage is not allowed there. A quick Babel preset wrapper might do the trick (no guarantees):

// babel-preset-mui.js
module.exports = function(api) {
  const supportsStaticESM = api.caller(caller => !!caller.supportsStaticESM);
  return {
    plugins: [
      ["babel-plugin-transform-mui-imports", {
        esm: supportsStaticESM
      }]
    ]
  };
}

@supervanya
Copy link

If anyone is importing alpha after updating to 4.12 here is an updated version:

// babel-plugin-transform-mui-imports.js
module.exports = function ({ types: t }) {
  return {
    name: "transform-mui-imports",
    visitor: {
      ImportDeclaration(path, state) {
        const source = path.node.source.value;
        if (!/(@material-ui\/(core|icons))$/.test(source)) return;
        path.replaceWithMultiple(path.get('specifiers').map(s => {
          let specificSource = source + (state.opts.esm===false ? '' : '/esm');
          let specifier = t.clone(s.node);
          // ignore wildcard and default imports that can't be exploded
          if (!t.isImportDefaultSpecifier(s) && !t.isImportNamespaceSpecifier(s)) {
            const name = s.node.imported.name;
            if (name === 'colors') {
              // import { colors } from '..' --> import * as colors from '/colors'
              specifier = t.importNamespaceSpecifier(specifier.local);
              specificSource += '/colors';
            }
            else if (/^(makeStyles|createStyles|createMuiTheme|responsiveFontSizes|styled|useTheme|withStyles|withTheme|createGenerateClassName|jssPreset|ServerStyleSheets|StylesProvider|MuiThemeProvider|ThemeProvider|easing|duration|hexToRgb|rgbToHex|hslToRgb|decomposeColor|recomposeColor|getContrastRatio|getLuminance|emphasize|fade|darken|lighten|alpha)$/.test(name)) {
              // core re-exports all of /styles
              // import { styled } from '..' --> import { styled } from '/styles'
              specifier = t.clone(s.node);
              specificSource += '/styles';
            }
            else {
              // import { List } from '..' --> import List from '/esm/List'
              specifier = t.importDefaultSpecifier(specifier.local);
              specificSource += '/' + name;
            }
          }
          return t.importDeclaration([specifier], t.stringLiteral(specificSource));
        }));
      }
    }
  };
}

I just added it to the repo and imported it in the babelrc.js:

// babelrc.js
const transformMuiImports = require('./babel-plugin-transform-mui-imports');

module.exports = { plugins: [transformMuiImports] };

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment