payloadcms / remix-server

Monorepo template with Remix and Payload
MIT License
106 stars 14 forks source link

Run production build without Turbo? #15

Closed 58bits closed 1 year ago

58bits commented 1 year ago

Confess I've tried just about everything I can think of - but I'm unable to run the production server without Turbo....

For example, in a layered Docker image we've created, I'd like to be able to call

CMD NODE_ENV=production node --conditions=serve ./apps/server/dist/index.js

...as the CMD for Docker.

But when we do this, we get remix require errors - such as

Error: Cannot find module 'form-data'
Require stack:
- /.../apps/remix/build/index.js
- /.../apps/server/dist/index.js

Placing the dependencies in Remix's serverDependenciesToBundle setting works, but there are a LOT of them, and this can't possibly be the right approach.

We've also tried (but currently failed) to NOT hoist the Remix node_modules installs, but can't seem to get this to work with pnpm. And we've also tried (and have reproduced the error) by calling

NODE_ENV=production node --conditions=serve ./apps/server/dist/index.js

...from our local repo (i.e. without Docker) and are assuming that if we can get this to work, Docker will be fine.

What 'magic' is Turbo using in order to call the serve script in apps/server without any dependency errors?

@dsod - any ideas?

(P.S - we've placed PAYLOAD_CONFIG_PATH in our .env file)

58bits commented 1 year ago

@kiliman - any guesses?

dsod commented 1 year ago

Hey! Building the project and then running npm run serve from the apps/server folder works fine for me. A difference is that by running serve through turbo the scripts "working folder" will be apps/server, whereas your CMD script above is run from the project root. Since you are not bundling dependencies into the remix server bundle, it will retrieve them from node_modules on runtime. Make sure to run the serve script from the server folder, and that the node_modules folders exist in the app folders inside the running container.

58bits commented 1 year ago

@dsod - Hi Daniel thanks so much for taking the time to reply as well as merging the PR!

I reached a point where I was printing out console.log(process.cwd() of every combination of directory location I could think of - including as you suggest, running serve from the apps/server folder.

When you say you ran npm run serve above - form the apps/server folder - do you mean you ran pnpm run serve? Or did you really run npm run serve?

Our goal was to call the transpiled apps/server/dist/server.js file directly - like this...

PAYLOAD_CONFIG_PATH=../cms/dist/payload.config.js NODE_ENV=production node --conditions=serve dist/index.js

...as the start command for our Docker image (actually from supervisord), and it's this form we're having trouble with - whether called in Docker, or called in the local repo.

but at the moment - whenever we do this.. we get the following (edited to remove local context)

Error: Cannot find module 'form-data'
Require stack:
- /apps/remix/build/index.js
- /apps/server/dist/index.js
    at Function.Module._resolveFilename (node:internal/modules/cjs/loader:1075:15)
    at Function.Module._load (node:internal/modules/cjs/loader:920:27)
    at Module.require (node:internal/modules/cjs/loader:1141:19)
    at require (node:internal/modules/cjs/helpers:110:18)
    at Object.<anonymous> (/apps/remix/build/index.js:10448:32)
    at Module._compile (node:internal/modules/cjs/loader:1254:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1308:10)
    at Module.load (node:internal/modules/cjs/loader:1117:32)

form-data is a dependency in our Remix app - but it's not just form-data - it's a long list of 'require' dependencies.

Actually we get the same error if we run pnpm run serve or even npm run serve from the apps/server directory

The only time we don't get any errors at all, is when we run pnpm run serve from the root directory, and then turbo executes the command for us. And so somehow the combination of pnpm, hoisted node_modules and turbo 'just works' - but I have no idea why. Heh.

dsod commented 1 year ago

Hey,

Any time! Ah yes, pnpm, of course. I will reach out on Discord and we can discuss further there.

58bits commented 1 year ago

@dsod Awesome thanks - also noticed that the actually error was missing the error message above - it's there now. Which Discord groups are you in? Turbo? Payload? or Remix?

dsod commented 1 year ago

Hey, I wrote a DM to you. You can find me in the Payload and Remix servers otherwise.

dsod commented 1 year ago

TL;DR When you want or have to specify a dependency in remix's serverDependenciesToBundle, and node is unable to resolve transitive dependencies of that package, configure pnpm to "publicly" hoist it: https://pnpm.io/npmrc#public-hoist-pattern

Explanation When specifying packages in the remix.config.js > serverDependenciesToBundle: [...], esbuild will include said package in the remix server build, and try to resolve and include the package nested/transitive dependencies in the bundle as well. Sometimes, esbuild is unable to resolve transitive packages which results in them not being included in the bundle.

When this happends, and node tries to import/resolve one of the transitive packages, it will fail to do so when using the default pnpm node_modules setting. Long story short, pnpm "hides" packages in node_modules/.pnpm/... (for good reason) while node tries to find the package in node_modules/.... So the module can't be found.

By configuring pnpm to publicly hoist the transitive package, it will be placed directly inside the node_modules folder, and node will be able to resolve it.

58bits commented 1 year ago

Thanks @dsod - this is brilliant!