Search code examples
typescriptparceljssolid-js

Using SolidJS with Parcel & @suid/material build fails


I've tried to use the SolidJS+Parcel+TypeScript template starter project from here: https://github.com/TiagoCavalcante/solidts-parcel

And once I import any element from @suid/material, such as Button, build fails.

Here's how to reproduce:

  1. Checkout project template from the about URL.
  2. npm i --save @suid/material
  3. Put the following in index.tsx:
import { render } from "solid-js/web";
import SuidButton from '@suid/material/Button'

function App() {
    return <>
    <SuidButton>Testing</SuidButton>
    </>;
}

render(App, document.getElementById("app")!);

You will get an error output:

🚨 Build failed.

@parcel/transformer-js: Expected ',', got 'ownerState'

  C:\Users\alona\dev\parcel\node_modules\@suid\material\Button\Button.jsx:307:25
    306 |     const contextProps = useContext(ButtonGroupContext);
  > 307 | Root ownerState={allProps} className={clsx(classes.root, otherProps.className, contextProps.className)} disabled={props.disabled} focusRipp
  >     |      ^^^^^^^^^^
    308 |       <Show when={!!props.startIcon}>
    309 |         <ButtonStartIcon className={classes.startIcon} ownerState={allProps}>

My question is, how can I make it play well with @suid/material?
it works fine when using ViteJS instead of Parcel

This issue seems related:
https://github.com/parcel-bundler/parcel/issues/6176
I asked on their discord, and apparently Parcel doesn't really support non-standard JS syntax in node_modules (like Typescript or JSX).

This means, that @suid/material cannot be used with Parcel, as well as others such as https://github.com/LXSMNSYC/solid-headless


Solution

  • There is no officially supported parcel template but you don't need one.

    Here is a basic setup that uses typescript and supports Hot Module Replacement. I did not tested for @suid/material but with it should work after you add necessary dependencies.

    1. Install babel-preset-solid and @babel/core.

    Parcel automatically picks up babel configuration but it is best to stick to the json based configuration. There is no need for babel-transform-runtime and preset-env since parcel provides its own alternatives:

    Parcel supports both project wide config files such as babel.config.json, as well as file relative configs such as .babelrc.

    Note: JavaScript Babel configs (e.g. babel.config.js) should be avoided. These cause Parcel’s caching to be less effective, which means all of your JS files will be recompiled each time you restart Parcel. https://parceljs.org/languages/javascript/#babel

    1. Create a .babelrc file with the following content:
    {
      "presets": ["solid"]
    }
    
    1. Add typescript file:
    {
      "compilerOptions": {
        "jsx": "preserve",
        "jsxImportSource": "solid-js",
        "noEmit": true
      }
    }
    
    1. Add hot module replacement (HMR) support if you like. This is a very crude way to add HRM and it loads module as expected but unfortanetly does not preserve the state:
    import { render } from 'solid-js/web';
    import { App } from './App';
    const dispose = render(() => <App />, document.body);
    
    if (module.hot) {
      module.hot.accept();
      module.hot.dispose(dispose);
    }
    

    One minor issue I know of is ParcelJS doesn't support conditional exports yet, which makes ParcelJS load the production build of SolidJS instead of its development build. https://github.com/solidjs/solid-refresh#other-dev-servers

    This is not a shortcoming on Parcel's part but an issue due to NodeJS's arbitrary solutions to some common compatibility problems, which can be solved with a plugin.

    You can check the link for contents and file structures: https://stackoverflow.com/a/75641238/7134134