swordev / suid

A port of Material-UI (MUI) built with SolidJS.
https://suid.io
MIT License
680 stars 49 forks source link

SSR: Could not resolve "@suid/material" when trying to bundle using ESBuild #186

Open Bersaelor opened 1 year ago

Bersaelor commented 1 year ago

Hello,

Attempt 1

so I have a working SSR solution, where I'm bundling up a solid.s build using aws sam. Internally aws sam builds each folder as a single module using esbuild.

I'm using rollup to bundle the solidjs site into a module, which can then be built by esbuild.

The intermediate result of the `rollup` looks like the following: ```js 'use strict'; var web = require('solid-js/web'); var material = require('@suid/material'); var solidJs = require('solid-js'); const _tmpl$$4 = ["", "
"]; const GradientBack = props => { return web.ssr(_tmpl$$4, web.ssrHydrationKey(), "background:" + "linear-gradient(180.2deg, #4E6B76 0.14%, #94A6AC 99.8%), #FFFFFF" + (";flex:" + 1), web.escape(props.children)); }; // .. lots of bundled rolled up code ```

but when I try to build this with esbuild, I get:

Build Failed
Error: NodejsNpmEsbuildBuilder:EsbuildBundle ✘ [ERROR] Could not resolve "@suid/material"

    ssg/lib/index.js:5:23:
      5 │ var material = require('@suid/material');

Before I added @suid/material it still built fine and worked. I wonder if the error is because the node_modules/@suid folder looks so different (having an index.jsx file with all the exports) compared to the node_modules/solid-js folder. The solid-js folder has a package.json and a dist subfolder.


Attempt 2

Now, the above file is created by using rollup -c rollup.config.js on a pretty standard Solid-js setup.

`rollup.config.js`: ```js import jsx from 'acorn-jsx'; import nodeResolve from "@rollup/plugin-node-resolve"; import common from "@rollup/plugin-commonjs"; import babel from "@rollup/plugin-babel"; export default [ { input: "index.jsx", output: [ { dir: "lib", exports: "auto", format: "cjs" } ], external: ["solid-js", "solid-js/web", "@suid/material/styles", "@suid/material"], acornInjectPlugins: [jsx()], plugins: [ nodeResolve({ preferBuiltins: true, exportConditions: ["solid", "node"] }), babel({ babelHelpers: "bundled", presets: [["solid", { generate: "ssr", hydratable: true }]] }), common() ] } ]; ```

One alternative idea I had was to not declare @suid external and roll up it up with the rest, i.e. only

    external: ["solid-js", "solid-js/web"],

When I do this, the rollup -c rollup.config.js fails on importing ./styles:

[!] Error: Could not resolve './styles' from ../site/node_modules/@suid/material/useMediaQuery.js
Error: Could not resolve './styles' from ../site/node_modules/@suid/material/useMediaQuery.js
    at error (/../rollup/dist/shared/rollup.js:198:30)
    at ...
error Command failed with exit code 1.

which is caused by

import { useTheme } from "./styles";

being the first line in useMediaQuery.js.

Now I'm not an expert in how node imports modules, so I'm kind of lost of what I could do to get suid imported into my project.

Bersaelor commented 1 year ago

PS: Now, as for Attempt 2 above, if I rename all *.jsx to *.js , i.e. running:

for x in node_modules/@suid/**/*.jsx; do mv \"$x\" \"${x%.jsx}.js\"; done

on the node_modules before the second approach above, I actually can get rid of a lot of the import-problems.

To be fair, for some reason the global rename command above takes 20s to run, not great for building new site versions.

So yeah, if I rename allt he jsx to js I can build a valid bundle for SSR. Unfortunately it'll now fail during normal vite development work.

juanrgm commented 1 year ago

The current module system (ESM/CommonJS) is insane and I had a lot of trouble finding a balance.

There may be changes in the future but I have not decided yet due to the problems they present. I'll wait for TypeScript 5 with its new module resolutions to see how it goes.

The main reason for the current distribution file structure (https://www.npmjs.com/package/@suid/material?activeTab=explore) is to favor direct import (no dist folder involved). Yes tree shaking exists, but it's useless when you're developing, as it involves scanning the entire file (imagine that with @suid/icons-material). That's why @suid/vite-plugin optimizes imports.

Specifying the available imports in the package.json file is the right way to go, but last time I tried it the TypeScript integration was a disaster (IntelliSense).

In summary, the specifications of how to distribute the packages should comply with the following:

  1. Direct imports (import Box from "@suid/material/Box").
  2. Imports via index (import { Box } from "@suid/material").
  3. Avoid having to add the file extension on all imports into the SUID repository (ex: import "./file.jsx").
  4. Avoid dual packages (ESM/CommonJS) or bundling that increase package size.
  5. Of course, auto-suggestion on all imports with named specifiers in VSCode and typing of direct imports.
  6. Avoid leaving K.O. to vite with imports through the index.