remix-run / remix

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

Cannot run `remix run` on multiple apps at once #296

Closed chaance closed 1 year ago

chaance commented 3 years ago

Because the port used by the WebSocket server is hard-coded, there's currently no way to concurrently run dev for two Remix apps at once.

https://github.com/remix-run/remix/blob/c1821e4f2c7e844f3b7462bbd6ae7711479c1c7d/packages/remix-dev/cli/commands.ts#L52

The run and watch commands would need to be updated to accept an alternative argument to set the WebSocket's server port. It would be nice if we could also pass these as command line arguments as needed.

# easily override either the port passed to @remix-run/serve or the watch command
# bikeshed the APIs, obviously!
$ remix run --port 3030 --socket-port 3002

Thoughts?

jacob-ebey commented 3 years ago

The dev CLI now honors process.env.PORT for where to start your app, and remix.config.js devServerPort for the asset / reload server. I believe this can be closed unless we decide we want to add flags as another config point. @chaance what are your thoughts here?

sergiodxa commented 3 years ago

Is that process.env.PORT only used for the main server port? If that's the case I think this is not solved, the issue here is the WebSocket server used for live reload is hardcoded to 3001, so if you have two Remix apps running let's say port 3000 and 4000, both will try to use the WebSocket server on port 3001.

FedericoBiccheddu commented 3 years ago

Another problem arises if you are working under https. ~Cannot open non-secure sockets (ws)~ EDIT: I was wrong, opening the port it does create a ws.

I was trying to monkey-patch the LiveReload component, but currently struggling with a solution that works both server and client side.

A possible solution could be:

let ws = new WebSocket("${window.location.protocol === 'https' ? 'wss' : 'ws'}://${window.location.hostname}:${port}/socket");

But don't know/understand internals very well, so I don't get the reason because LiveReload is loaded even server side.

chaance commented 3 years ago

The dev CLI now honors process.env.PORT for where to start your app, and remix.config.js devServerPort for the asset / reload server. I believe this can be closed unless we decide we want to add flags as another config point. @chaance what are your thoughts here?

I think the CLI flags for both are useful and should take precedence over values in config, personally. Thinking here in cases where I just want to run the app quickly but something else is running in the background. It'd also be nice if instead of just dropping the process, Remix could be smart and just look for another port if the default/configured port is in use (though I imagine we'd want a way to turn off that behavior).

sergiodxa commented 3 years ago

It'd also be nice if instead of just dropping the process, Remix could be smart and just look for another port if the default/configured port is in use

This would be really useful so you don't need to care about that at all

wmik commented 2 years ago

Had an issue closely related to this a while ago.

Problem: Live reload wasn't working and the server and needed to be restarted to reload the changes. Issue: Turns out chrome was forwarding ALL CONNECTIONS on localhost to HTTPS Solution: Adjust domain security policies for localhost to prevent auto redirect to https (explanation)

Unfortunately this started happening again with remix version 1.2.2 however on close inspection I noticed something different so now it's

Problem: Live reload not working when developing on HTTPS Issue(possibly): Live reload component attempting to connect based on protocol as outlined by @FedericoBiccheddu


Another problem arises if you are working under https. ~Cannot open non-secure sockets (ws)~ EDIT: I was wrong, opening the port it does create a ws.

I was trying to monkey-patch the LiveReload component, but currently struggling with a solution that works both server and client side.

A possible solution could be:

let ws = new WebSocket("${window.location.protocol === 'https' ? 'wss' : 'ws'}://${window.location.hostname}:${port}/socket");

But don't know/understand internals very well, so I don't get the reason because LiveReload is loaded even server side.

I'm no networking/server/sockets expert but I believe in order to connect to a websocket server using the wss protocol... The websocket server needs an https server to facilitate that as documented here.

Unfortunately that doesn't seem to be the case here https://github.com/remix-run/remix/blob/c66a5a807cf07d6899751f19ef658fe24554c726/packages/remix-dev/cli/commands.ts#L90

Even though the proposed solution is already implemented https://github.com/remix-run/remix/blob/c66a5a807cf07d6899751f19ef658fe24554c726/packages/remix-react/components.tsx#L1389

Went ahead and modified my installation in the node_modules folder to

let protocol = "ws:";

and it seems to solve the issue

Maybe I'm missing something (e.g can I configure/attach an https server somehow?) but any chance someone could take a look or share an alternative fix Thanks in advance and 🙇 🎉 love and appreciate all the work and effort that's gone and going into this project this is truly a wonderful project and tool. Can't imagine my workflow without it now😂

Cheers


What version of Remix are you using?

1.2.2

Steps to Reproduce

Expected Behavior

Live reload works when changes are applied to the code in the app folder

Actual Behavior

Live reload does not work/restart because it is unable to connect to the websocket server via wss without an HTTPS server

Browser inspect console error when app starts

(index):26 WebSocket connection to 'wss://localhost:8002/socket' failed: 
(anonymous) @ (index):26

Remix dev asset server web socket error:
(index):43 Event
isTrusted: true
bubbles: false
cancelBubble: false
cancelable: false
composed: false
currentTarget: WebSocket {url: 'wss://localhost:8002/socket', readyState: 3, bufferedAmount: 0, onopen: null, onerror: ƒ, …}
defaultPrevented: false
eventPhase: 0
path: []
returnValue: true
srcElement: WebSocket {url: 'wss://localhost:8002/socket', readyState: 3, bufferedAmount: 0, onopen: null, onerror: ƒ, …}
target: WebSocket {url: 'wss://localhost:8002/socket', readyState: 3, bufferedAmount: 0, onopen: null, onerror: ƒ, …}
timeStamp: 1083.9000000003725
type: "error"[[Prototype]]: Event
ws.onerror @ (index):43
jamesmall commented 2 years ago

Pilfered through the source code and found that you can set it via command line. If you update the devServerPort in the remix.config.js file in conjunction with setting REMIX_DEV_SERVER_WS_PORT via command line you can run multiple apps at once with live reload. You would also have to set the PORT to be different than the other app, of course.

// remix.config.js
...
module.exports = {
  appDirectory: 'app',
  browserBuildDirectory: 'public/build',
  publicPath: '/build/',
  serverBuildDirectory: 'build',
  devServerPort: [NEW WS PORT],
};

// command line
PORT=3001 REMIX_DEV_SERVER_WS_PORT=[NEW WS PORT] remix dev

The issue can be found in @remix-run/dev/config.js file. The config is being read but for whatever reason, the devServerPort isn't being set on appConfig causing the devServerPort to be the new port when compiled but the ws port remains 8002.

mcansh commented 2 years ago

Pilfered through the source code and found that you can set it via command line. If you update the devServerPort in the remix.config.js file in conjunction with setting REMIX_DEV_SERVER_WS_PORT via command line you can run multiple apps at once with live reload. You would also have to set the PORT to be different than the other app, of course.

// remix.config.js
...
module.exports = {
  appDirectory: 'app',
  browserBuildDirectory: 'public/build',
  publicPath: '/build/',
  serverBuildDirectory: 'build',
  devServerPort: [NEW WS PORT],
};

// command line
PORT=3001 REMIX_DEV_SERVER_WS_PORT=[NEW WS PORT] remix dev

The issue can be found in @remix-run/dev/config.js file. The config is being read but for whatever reason, the devServerPort isn't being set on appConfig causing the devServerPort to be the new port when compiled but the ws port remains 8002.

you are absolutely right, I've resolved it here :) #2456

seems like the extra function that we .toString() causes it to lose all awareness of the port being passed

kiliman commented 2 years ago

The funny thing is that the WebSocket server port is the one that I don't care about and Remix should just pick a random port.

Oddly though, Remix App Server was updated to pick a random port if the default was already open. This is rarely what I want. I'd much rather RAS just exit with error and force me to pick a PORT.

mcansh commented 2 years ago

The funny thing is that the WebSocket server port is the one that I don't care about and Remix should just pick a random port.

it does, it just wasn't making it to LiveReload 🙈

github-actions[bot] commented 2 years ago

🤖 Hello there,

We just published version v1.3.5-pre.1 which involves this issue. If you'd like to take it for a test run please try it out and let us know what you think!

Thanks!

MichaelDeBoey commented 2 years ago

Closing as #2456 is merged now

mcansh commented 2 years ago

Closing as #2456 is merged now

that didn't fix it 😢

machour commented 2 years ago

Fixed by #3447 ?

machour commented 1 year ago

Should be ok by now, please reopen if not.

abubakarasifmughal commented 1 month ago

Why change the package.json scripts by adding more Env Vars I linked my .env as following, feel free to add you port if you don't want to link .env file

export default {
  tailwind: true,
  ignoredRouteFiles: ['**/.*'],
  dev: {
    port: Number(process.env.REMIX_DEV_PORT),
  },
  // appDirectory: "app",
  // assetsBuildDirectory: "public/build",
  // serverBuildPath: "build/index.js",
  // publicPath: "/build/",
  watchPaths: () => createWatchPaths(__dirname),
};