Search code examples
javascripttypescriptmaterial-uies6-modulesparcel

How to properly build a design system using material ui


in our company we are trying to build our design system as a package, in order to install it in several of our projects. Building the package works fine, but once we install it and import something from it, we get the following error:

ERROR in ../design-system/dist/main.js 5:0-54
Module not found: Error: Can't resolve '@mui/material/Tab' in '/Users/***/***/minimal-example/frontends/design-system/dist'
Did you mean 'index.js'?
BREAKING CHANGE: The request '@mui/material/Tab' failed to resolve only because it was resolved as fully specified
(probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.

I have managed to create a quite small project to reproduce the problem, you can find it here: https://github.com/antoniogamizbadger/minimal-example

To reproduce the problem, you just need to clone it and follow these steps:

From design-system directory:

npm ci
npm run build

From package directory:

npm ci
npm start

We have tried several things but now we are clueless why this is happening. The error message suggests some of the root problems. Nonetheless, we have tried to fix it/research the error without finding any solution.

Any idea what configuration should we change to make this really easy use case work?


Solution

  • Your design-system package declares that it is an ESM module via "type": "module" in the package.json.

    Tooling has got a fair amount stricter in recent years in forcing spec-compliance. In your case, it's Webpack being strict about it. But you should be compliant on shared libs anyway. More modern bundlers like Vite try to cover this up a bit but in general, the situation right now as we transition into ESM is a bit of a mess with different tools doing different things.

    When you import like this:

    import TabMaterial  from '@mui/material/Tab'
    

    It is a import of a singular file and not a module, so within ESM's spec that would have to be:

    import TabMaterial  from '@mui/material/Tab.js'
    

    Generally, though you shouldn't do this anyway. The internal file structure of Mui is private and liable to change. Just use:

    import { Tab as TabMaterial } from '@mui/material'
    

    You don't need to worry about the effects this has on bundle size.