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

Viewed 2148

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",
  },
...
2 Answers

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']
};

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

Related