4

Is it possible to have Material UI as peerDependency but keep its types as DevDependency?

I am building a component library using React + Typescript and the components are based on Material UI library, with Rollup as module bundler.

Here is an example of my IInputProps type extending Material UI TextFieldProps type.

import { TextFieldProps } from "@material-ui/core";

export type IInputProps = TextFieldProps & {...}

My goal is to set Material UI packages as a peerDependency, so it will use the material-ui package installed on the target project. I inserted the Material UI as peerDependency and set up rollup with peerDepsExternal plugin. When I tried to build the package, it throws the following error:

Cannot find module '@material-ui/core' or its corresponding type declarations.

The reason is related to this answer (What's the relationship of @material-ui/core and @types/material-ui?). Material-UI package contains its own type definitions (*.d.ts files), so when I set it as peerDependency, the types/interfaces are missing. To solve this problem, I followed this solution (https://github.com/ezolenko/rollup-plugin-typescript2/issues/198) declaring each module on a src/@types/material-ui/index.d.ts file, but it raised another problem: I cannot use material-ui types/interfaces anymore.

With @material-ui/core declared on my src/@types/material-ui/index.d.ts file, the code points this error below.

Cannot use namespace 'TextFieldProps' as a type.ts(2709)

Exported type alias 'IInputProps' has or is using private name 'TextFieldProps'.ts(4081)

Now, I can only see these 2 solutions:

  1. Keep the entire material ui packages on my library and lose in the package size:
Library with Material UI packages as peerDependency
npm notice package size:  101.0 kB
npm notice unpacked size: 493.6 kB

Library with Material UI packages
npm notice package size:  1.2 MB
npm notice unpacked size: 6.0 MB
  1. Use Material UI as peerDependency, but lose the Material UI types/interfaces and lint (by adding a property to my types/interfaces that accept any prop - TypeScript interface that allows other properties).
[x:string]:any

So, I would like to know if there is any way to have Material UI as peerDependency but keep its types as devDependencies, so I can bundle it with Material UI as peerDependency and also use its types on my package?

My configurations are following below:

src/@types/material-ui/index.d.ts

declare module "@material-ui/core";

rollup.config.js

import peerDepsExternal from "rollup-plugin-peer-deps-external";
import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import image from "@rollup/plugin-image";
import typescript from "rollup-plugin-typescript2";

const packageJson = require("./package.json");

export default {
  input: "src/index.ts",
  output: [
    {
      file: packageJson.main,
      format: "cjs",
      sourcemap: true
    },
    {
      file: packageJson.module,
      format: "esm",
      sourcemap: true
    }
  ],
  plugins: [
    peerDepsExternal(),
    resolve(),
    commonjs(),
    image(),
    typescript({ useTsconfigDeclarationDir: true })
  ]
};

tsconfig.json

{
  "compilerOptions": {
    "rootDir": "src",
    "declaration": true,
    "declarationDir": "build",
    "module": "esnext",
    "target": "es5",
    "lib": ["es6", "dom", "es2016", "es2017"],
    "sourceMap": true,
    "jsx": "react",
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "typeRoots": ["./src/@types"]
  },
  "include": ["src/**/*"],
  "exclude": [
    "node_modules",
    "build",
    "storybook-static",
    "src/**/*.stories.tsx",
    "src/**/*.test.tsx"
  ]
}

package.json

...
"main": "build/index.js",
  "module": "build/index.esm.js",
...
,
  "peerDependencies": {
    "@material-ui/core": "^4.11.4",
    "react": "^16.8.0",
    "react-dom": "^16.8.0",
  },
...
  • Why don't you add types as dev dependencies? If i'm understanding right types not bundled anyway. – Aleksandr Smyshliaev Jul 29 '21 at 18:24
  • @AleksandrSmyshliaev there is no way to get the @material-ui/core types separated from its package. That would certainly solve the problem if it was possible (check this link: https://stackoverflow.com/questions/52194069/whats-the-relationship-of-material-ui-core-and-types-material-ui/52194095) – André Borba Netto Assis Jul 29 '21 at 18:43
  • Can you try create some folder, npm init, add material-ui as a devDep there and use material ui from that folder. – Aleksandr Smyshliaev Jul 29 '21 at 18:52

2 Answers2

2

If you expect to use an "external" @material-ui/core (i.e. not part of the rollup), you should specify it as such.

Rollup actually has documentation on peer dependencies, which does specify to use externals, for example with lodash:

// rollup.config.js
import resolve from '@rollup/plugin-node-resolve';

export default {
  input: 'src/main.js',
  output: {
    file: 'bundle.js',
    format: 'cjs'
  },
  plugins: [resolve({
    // pass custom options to the resolve plugin
    customResolveOptions: {
      moduleDirectory: 'node_modules'
    }
  })],
  // indicate which modules should be treated as external
  external: ['lodash']
};
Kelvin Schoofs
  • 8,323
  • 1
  • 12
  • 31
  • Unfortunately this solution is not complete. I just dig more into the problem and It looks like the Rollup or some plugin related has a bug when building at first time a package with material-ui as peerDependencies. If I call rollup twice, so on the first-time material-ui is NOT included as peerDependencies and at the second time material-ui is included as peerDependencies, then it works. I created this repository with a minimal scenario to replicate the bug. You can clone, run yarn && yarn build to see the error. https://github.com/andrebnassis/rollup-material-ui-peerDependencies-error – André Borba Netto Assis Aug 03 '21 at 18:19
  • I am using 'rollup-plugin-peer-deps-external' plugin, that makes rollup identifies all my peerDependencies declared on package.json, so I do not need this 'external' rollup configuration. But thanks for the effort! :) – André Borba Netto Assis Aug 05 '21 at 22:58
  • A note for anyone that my be wondering why mui is not working as a peerDependency when other things are: There is a bug (https://github.com/ezolenko/rollup-plugin-typescript2/issues/277) filed against the specific and commonly used rollup plugin "rollup-plugin-typscript2". "@mui/core" fails as a peerDependency, and must ALSO be listed as devDependecy. (They've closed the bug even though they only offer this workaround) – Artemis Prime Jan 31 '22 at 14:33
2

I solved the problem by setting all my peerDependencies also as my devDependencies.

So, my package json was setting as below:

"peerDependencies": {
    "@material-ui/core": "^4.11.4",
    "react": "^16.8.0",
    "react-dom": "^16.8.0",
},
"devDependencies": {
    "@material-ui/core": "^4.11.4",
    "react": "^16.8.0",
    "react-dom": "^16.8.0",
...

Then I could get rid off some configurations:

  1. I deleted my 'src/@types/material-ui/index.d.ts'
  2. I removed ' "typeRoots": ["./src/@types"]' from my tsconfig.json

All the others configurations remained the same.

reference: https://github.com/HarveyD/react-component-library/issues/40