remix-run / remix

Build Better Websites. Create modern, resilient user experiences with web fundamentals.
https://remix.run
MIT License
29.93k stars 2.52k forks source link

[vite] Files shared between Remix and Express are loaded twice #9202

Open duailibe opened 7 months ago

duailibe commented 7 months ago

Reproduction

https://stackblitz.com/edit/remix-run-remix-muutqq?file=server.js,app%2Froutes%2F_index.tsx

Check server.js and routes/_index.tsx (there are comments indicating where).

System Info

System:
    OS: macOS 13.5.2
    CPU: (8) arm64 Apple M1
    Memory: 109.50 MB / 8.00 GB
    Shell: 3.6.1 - /opt/homebrew/bin/fish
  Binaries:
    Node: 18.19.1 - ~/.local/share/mise/installs/node/18.19/bin/node
    npm: 10.2.4 - ~/.local/share/mise/installs/node/18.19/bin/npm
    pnpm: 8.7.5 - /opt/homebrew/bin/pnpm
  Browsers:
    Chrome: 123.0.6312.106
    Safari: 16.6

Used Package Manager

npm

Expected Behavior

In the StackBlitz, I'd expect that all imports of foo-service would resolve to the same memory reference.

Actual Behavior

When importing the same module in express code and in Remix code, the module gets loaded "twice" (once the actual module, and the other is its copy bundled in build/server/index.js)

I tried using ssr.external but that doesn't seem to work with relative files, only node_modules?

With the classic compiler, I was able to work by using the server option in remix.config.js, but I can't find a way to that with Vite (I'm not well versed with Vite's options though). If there's any workaround to this problem I'd appreciate. Otherwise, I won't be able to use the new Vite compiler at all in my projects

kiliman commented 7 months ago

Interesting. Yeah, I thought that importing directly from app/services from my server file would work, but you're right. The generated build/server/index.js file contains a copy of the imported code.

Anyway, I played around with a bunch of Vite and esbuild flags, but never could get them to combine since they're not built together.

So I got the crazy idea of simply having Remix import my Express server file directly. My server file actually exports the express app (for use in my Vite plugin)

// app/entry.server.tsx
import app from "#server/index";  // import the created express app
export { app }

// rest of entry.server.tsx

Had Remix build the production file and sure enough, the generated file included both my Express code and the Remix stuff.

Ran NODE_ENV=production node ./build/server/remix.js

Voila!

image
duailibe commented 7 months ago

Thanks @kiliman, that's actually not a bad idea

I was able to make it work by (ab)using entry.server.tsx:

It's cool that I get HMR in the server for my entire app (both express and Remix parts)!

You can see the resulting code here: https://stackblitz.com/edit/remix-run-remix-t9pwxg?file=server.js,app%2Fentry.server.tsx

I'm not sure there's anything in Remix side that could be done to make this better, at least make sure the build file won't change too much in the future (but if it does change, that would be easy to catch in dev because it will fail fast). Either way, I do think this is a reasonable workflow and should be supported by Remix somehow.

PS: One thing I noticed is that the server build file exports some of my components and hooks. I could not understand why that happens, but got worried I could get a name conflict with the app in entry.server.tsx

mc-dsk commented 2 months ago

Any news ? Building with rollupOptions.external work. But ssr.external doesn't work.

So build : OK but HMR : KO