Rollup bundling peer dependencies / Multiple instances of MUI make the ThemeProvider not work
Asked Answered
S

0

6

I have two different repos I'm working on, lib and apps.

  • lib has some React components made with MUI that I want to consume in apps, and that's built with Rollup.

  • apps is a Lerna monorepo with a Next.js app that imports lib as a dependency to consume its components, which can be customized wrapping them in a <ThemeProvider> with a custom theme.

However, that doesn't work because there are duplicated instances of MUI / ThemeProvider, which I was able to verify by adding this to lib's entry point:

if (process.browser) {
  (window as any)._React = React;
  (window as any)._ThemeProvider = ThemeProvider;
}

And then this into apps':

if (process.browser) {
  const {
    _React,
    _ThemeProvider,
  } = window as any;

  console.log(_React ? (_React === React ? "✅ Unique React" : "❌ Duplicate React") : "-");
  console.log(_ThemeProvider ? (_ThemeProvider === ThemeProvider ? "✅ Unique ThemeProvider" : "❌ Duplicate ThemeProvider") : "-");
}

Which prints:

✅ Unique React
❌ Duplicate ThemeProvider

lib's component usage in app looks like this:

<ThemeProvider theme={ MY_CUSTOM_THEME }>
  <MyComponent{ ...myComponentProps } />
</ThemeProvider>

Where both MyComponent and MY_CUSTOM_THEME are imported from lib, while ThemeProvider is imported from @mui/material/styles, just like it is in lib.

However, all MUI components will be displayed with the default theme.

Here are some of the relevant build files for both repos:

lib > package.json:

{
  "name": "@myscope/lib",
  "version": "1.0.0-alpha",
  "description": "",
  "main": "dist/cjs/src/index.js",
  "module": "dist/esm/src/index.js",
  "types": "dist/index.d.ts",
  "files": [
    "dist"
  ],
  "private": true,
  "scripts": {
    "build": "rollup -c",
  },
  "dependencies": {
    "@apollo/client": "^3.4.5",
    "@apollo/link-context": "^2.0.0-beta.3",
    "@hookform/resolvers": "^2.8.5",
    "@mui/icons-material": "^5.0.4",
    "@swyg/corre": "^1.0.1",
    "apollo-upload-client": "^16.0.0",
    "atob": "^2.1.2",
    "axios": "^0.24.0",
    "btoa": "^1.2.1",
    "country-codes-list": "^1.6.8",
    "country-region-data": "^1.6.0",
    "next": "^11.0.1",
    "next-images": "^1.8.2",
    "openpgp": "^5.0.0",
    "react-hook-form": "^7.22.0",
    "react-payment-inputs": "^1.1.8",
    "react-use-country-region": "^1.0.0",
    "styled-components": "^5.3.0",
    "use-callback-ref": "^1.2.5",
    "uuidv4": "^6.2.12",
    "yup": "^0.32.11"
  },
  "peerDependencies": {
    "@mui/material": "^5.0.4",
    "react-dom": "^17.0.2",
    "react": "^17.0.2"
  },
  "devDependencies": {
    "@auth0/auth0-react": "^1.6.0",
    "@babel/preset-react": "^7.16.7",
    "@emotion/react": "^11.5.0",
    "@emotion/styled": "^11.3.0",
    "@graphql-codegen/cli": "^1.21.5",
    "@graphql-codegen/introspection": "^1.18.2",
    "@graphql-codegen/typescript-operations": "^1.18.0",
    "@graphql-codegen/typescript-react-apollo": "^2.2.5",
    "@graphql-codegen/typescript": "^1.22.1",
    "@mui/material": "^5.2.8",
    "@rollup/plugin-babel": "^5.3.0",
    "@rollup/plugin-commonjs": "^21.0.1",
    "@rollup/plugin-node-resolve": "^13.1.2",
    "@rollup/plugin-typescript": "^8.3.0",
    "@testing-library/dom": "^8.1.0",
    "@testing-library/jest-dom": "^5.14.1",
    "@testing-library/react": "^12.0.0",
    "@testing-library/user-event": "^13.2.1",
    "@types/jest": "^26.0.24",
    "@types/react-dom": "^17.0.11",
    "@types/react": "^17.0.16",
    "@types/styled-components": "^5.1.12",
    "@typescript-eslint/eslint-plugin": "^4.29.0",
    "@typescript-eslint/parser": "^4.29.0",
    "babel-jest": "^27.0.6",
    "babel-plugin-styled-components": "^2.0.2",
    "eslint-config-next": "^11.0.1",
    "eslint-config-prettier": "^8.3.0",
    "eslint-plugin-react-hooks": "^4.2.0",
    "eslint-plugin-unused-imports": "^1.1.2",
    "eslint": "^7.32.0",
    "graphql": "^16.2.0",
    "jest": "^27.0.6",
    "prettier": "^2.3.2",
    "rollup-plugin-dts": "^4.1.0",
    "rollup-plugin-node-externals": "^3.1.2",
    "rollup-plugin-peer-deps-external": "^2.2.4",
    "rollup-plugin-typescript2": "^0.31.1",
    "rollup": "^2.63.0",
    "ts-jest": "^27.0.4",
    "typescript": "^4.3.5"
  }
}

Note that @mui/material appears both in peerDependencies and devDependencies, but not react or rect-dom, which are only listed as peerDependencies. That's because there's @types/react and @types/react-dom, but nothing similar for MUI, so the Rollup build would fail if it can't find the right types.

lib > rollup.config.js:

import commonjs from '@rollup/plugin-commonjs';
import dts from 'rollup-plugin-dts'
import resolve from "@rollup/plugin-node-resolve";
import typescript from 'rollup-plugin-typescript2'
import babel from '@rollup/plugin-babel'

import pkg from "./package.json";

// Extensions handled by babel:
const EXTENSIONS = [".ts", ".tsx"];

// Exclude dev and peer dependencies:
const EXTERNAL = [
  ...Object.keys(pkg.devDependencies),
  ...Object.keys(pkg.peerDependencies),
];

export default [{
    input: 'src/index.ts',

    output: [{
        dir: "dist/esm",
        sourcemap: true,
        format: "esm",
        preserveModules: true,
        globals: {
          react: "React",
        },
    }, {
        dir: "dist/cjs",
        sourcemap: true,
        format: "cjs",
        preserveModules: true,
        globals: {
          react: "React",
        },
    }],

    external: EXTERNAL,
    
    plugins: [
       resolve(),

        commonjs({
            exclude: "node_modules",
            ignoreGlobal: true,
        }),

        typescript({
          useTsconfigDeclarationDir: true,
          tsconfig: "rollup.tsconfig.json",
        }),
    ],

}, {
    input: './dist/types/src/index.d.ts',
    output: [{ file: './dist/index.d.ts', format: "esm" }],
    external: EXTERNAL,
    plugins: [dts()],
}];

apps > lerna.json:

{
  "packages": ["apps/**/*", "packages/**/*"],
  "version": "0.0.1",
  "npmClient": "yarn",
  "useWorkspaces": true
}

apps > package.json:

...
"dependencies": {
    "@myscope/lib": "file:../lib"
},
...

Note this is a file: import just to make it easier to develop and test changes.

I've also tried adding resolutions to both apps > package.json and apps > app > package.json, but no luck with that either...:

"resolutions": {
  "@mui/material": "5.2.8"
}
Shawanda answered 14/1, 2022 at 12:23 Comment(4)
Haven't looked at this in depth, but noticed you have installed rollup-plugin-peer-deps-external, but don't appear to be using it in your rollup.config.jsWeidman
@RichardWilbur True, that was probably from me trying different options, but in rollup.config.js you can see there's external: EXTERNAL which should have the same effect.Shawanda
I don't think so. If you want to use the plugin, you'll need an import statement: import external from 'rollup-plugin-peer-deps-external'; and use it in the plugins section: // eslint-disable-next-line @typescript-eslint/no-unsafe-call external(), I have mine listed as the first plugin. There may be a reason for that. Also, I haven't found a way around the TypeScript warning, so thats why I disabled it. Hope that helps. Not sure if you're still having an issue or not. Just happened to see your post while trying to find an answer to the TS warningWeidman
Have you made any progress on this issue? :-( I've been stuck for two weeksEmmery

© 2022 - 2024 — McMap. All rights reserved.