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 inapps
, and that's built with Rollup.apps
is a Lerna monorepo with a Next.js app that importslib
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"
}