Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remix Dev Server #4838

Closed
2 tasks done
ryanflorence opened this issue Dec 12, 2022 Discussed in #4664 · 12 comments
Closed
2 tasks done

Remix Dev Server #4838

ryanflorence opened this issue Dec 12, 2022 Discussed in #4664 · 12 comments
Assignees
Labels
proposal:remix Proposals from the Remix team.
Milestone

Comments

@ryanflorence
Copy link
Member

ryanflorence commented Dec 12, 2022

Discussed in #4664

Originally posted by ryanflorence November 21, 2022

PRs

Remix Dev Server

Instead of just a file watcher that writes files with remix watch, we should have a dev server receive requests, serve the asset, then proxy everything else to the real server. This should make building developer tooling better for us and our users better (live reload, HMR, faster incremental builds, forwarding build errors to a browser modal, etc.)

Background

Two important concepts drove today’s Remix’s npm run dev design:

  1. Development should match Production as much as possible. This leads to fewer surprises in production and also helps developers see the effect of code changes to the network tab. Many of today’s tools have wildly different looking network tabs in production vs. development.
  2. Apps own their server. In order to deploy anywhere, it’s important that Remix remain server agnostic, and remain “just a web fetch request handler” on the server.

How it works today: two processes

Two Processes need to run for remix development today: remix watch and a command to run a server like node server.js or wrangler dev etc.

  • remix watch: This isn’t actually a server, it’s a file watcher. It reads remix.config.js to know where to write the server and client build artifacts. It does not receive requests, it just spits out files where the web server is reading from.
  • actual server: The real server, like node server.js, requires the server build that remix watch created (it’s just a module in the end) and uses it as the request handler. When the page is viewed in the browser, the assets remix watch created are requested from the actual server, as long as it’s correctly configured to server them of course!

In other words: dev and production are identical when it comes to what happens when a request for a document or a static asset is made. The server (node server.js) serves everything. remix watchsimply created the modules for the server and client. In this way, Remix can be deployed anywhere.

The exception: remix dev + remix-serve

For developers who don’t care to run their own server, the remix-serve package creates an express app configured just enough to run a Remix app. It has an integration with the remix CLI to run both the watcher (from remix watch) and the express server in the same process.

Problems today

Requiring two processes is annoying. Many developers today are not used to owning so much of the stack (which is why we created remix-serve).

Ports and Live Reload. It’s actually surprisingly difficult to develop two Remix apps at the same time. When you have two processes you can’t simply provide a --port 3001 cli option. You may do wrangler dev --port 3000 for one app and nodemon server.js --port 3001 in another. That’s fine for the servers but the live reload web socket, that’s part of remix watchneeds to specify a port too, and that port needs to be known for both SSR (server bundles) and in the browser (client bundles). In order to get the correct web socket port for both remix watch and wrangler dev, we use an environment variable which is not only cumbersome but also super weird: it’s read at runtime for the server render (actual environment variable) but bundled as a static value into the client modules (not actually an environment variable!).

Perhaps in the live reload case, we could figure out a better way to do it (like a cli option that gets bundled directly into both client/server bundles) but it illustrates the point well that having two processes in development adds some complexity. In fact, there have been a bunch of breakages around live reload in various commits because folks both outside and inside of Remix because they didn’t understand all the moving parts.

Proposal

I’m proposing we change the way Remix works in development by introducing a development server. Instead of requests always coming to the real server in development (node server.js), requests come to a Remix development server.

  • This development server handles the client assets dynamically
  • Other requests are proxied to the real server

API

  • remix.config.js has a new dev field that indicates how to spawn your development server

    { "dev": "nodemon server.js" }
  • remix dev boots a development server that also spawns the real server

When a requests comes in, the dev server matches it against the remix.config for assets, if it matches, it serves the file (with some bundling). If it doesn’t match, it proxies the request to the real server.

Expected benefits

Having a persistent dev server should make all of the “DX” things we want to do easier to deal with because we get to receive the requests first.

  • Live reload ports should be easier (could just find a port and tell the child process and the client bundles what it is)
  • We have a persistent dev server so we can stop doing the require cache purge in remix-serve and the express adapter, just restart the child process when the build changes
    • This is very important for live reload and I’d expect HMR, so we don’t tell the browser the update is ready until it actually is. Many remix apps with concurrently and pm2-dev have race conditions where the browser reloaded when files changed but the builds aren't done!
  • In the future we can bundle only the assets that are needed for the request. Though esbuild is already very fast for bundling the entire application, we could keep it fast forever by only bundling the code path being rendered (client and server).
  • we’ll stop barfing a billion files into the build and public/build directories

The core benefit is that we are in control of when to notify the browser of anything with a persistent server that owns both processes. By spawning a child process for the real server, we also don’t lose the “development and production are nearly identical” either. All assets are bundled the same, served from the same URLs controlled by remix.config.

@ryanflorence ryanflorence added the proposal:remix Proposals from the Remix team. label Dec 12, 2022
@ryanflorence ryanflorence moved this to Backlog in Roadmap Dec 12, 2022
@kiliman
Copy link
Collaborator

kiliman commented Dec 13, 2022

One of the main complaints against Remix App Server is that you can't configure it to support SSL. Some devs want to use certificates on dev to match production. Will the new dev server allow you to set up a certificate? This also affects the web socket server.

@ryanflorence ryanflorence moved this from Backlog to Planned in Roadmap Dec 13, 2022
@ryanflorence ryanflorence moved this from Planned to Backlog in Roadmap Dec 13, 2022
@ryanflorence ryanflorence moved this from Backlog to Planned in Roadmap Dec 13, 2022
@sudomf
Copy link

sudomf commented Dec 15, 2022

It will make building developer tooling so much easier. Awesome 👏 👏 👏

@pcattori pcattori moved this from Planned to In Progress in Roadmap Dec 19, 2022
@cliffordfajardo
Copy link
Contributor

cliffordfajardo commented Dec 20, 2022

One of the main complaints against Remix App Server is that you can't configure it to support SSL. Some devs want to use certificates on dev to match production. Will the new dev server allow you to set up a certificate? This also affects the web socket server. - @kiliman

+1 ^

If you're self-hosting Remix ,the scenario of making your remix server an HTTPS server is more likely.
I setup 2 Remix apps at work and I needed to eject for both of them in order to add HTTPs support.

Others might say wonder? Why not put NGINX or a proxy server in front of your Remix server?
More infrastructure setup and complexity; not everyone can deploy to vercel / cloudflare if your company owns its own entire infra / data centers

@kiliman had a proposal enabling Remix App Server to support HTTPs without needing to eject
#4123

Maybe the HTTPS is out of scope, mostly bringing this up because I want every remix dev to have a smoother onboarding experience / integration with their infra 🤝 ⚛️

@jamesarosen
Copy link

jamesarosen commented Dec 20, 2022

Requiring two processes is annoying. Many developers today are not used to owning so much of the stack (which is why we created remix-serve).

Building on this: TailwindUI works by running its own process to continually process .{tsx,jsx,html} files and a .{js,ts} configuration file into CSS files. The current recommendation is to have Tailwind write to app/styles/app.css and git-ignore that generated file. This has a downside with the current remix-serve implementation:

  1. developer edits a .{jsx,tsx} file, adding a Tailwind class name to the component
  2. remix-watch sees the component change and begins recompiling
  3. simultaneously with 2, Tailwind sees the component change and begins recompiling
  4. remix-watch sees the CSS change and begins recompiling
  5. remix-serve issues a live-reload from 2
  6. remix-serve issues a live-reload from 4

(This may already be solved with a debounce. If so, this just serves as a reminder to make sure that protection makes it through to the new implementation.)

@kiliman
Copy link
Collaborator

kiliman commented Dec 20, 2022

Perhaps the new Dev Server can include an option for one or more processes to launch (including tailwind watcher for example).

npm run dev will then launch all configured dev processes concurrently instead of having to manually launch multiple processes or configure package.json scripts

Currently, there is devServerBroadcastDelay... which is used by <LiveReload>.

Maybe a devWatchBuildDelay to delay the build after a change has been detected.

So using the scenario above:

  1. developer edits a .{jsx,tsx} file, adding a Tailwind class name to the component
  2. remix-watch sees the component change and waits for devWatchBuildDelay
  3. simultaneously with 2, Tailwind sees the component change and generates CSS
  4. remix-watch sees the CSS change and waits for devWatchBuildDelay (cancel existing delay timer... so always delay a minimum of devWatchBuildDelay after every change)
  5. remix-serve delay timer expires so begins to build
  6. remix-serve issues a live-reload after build (from 5)

@ryanflorence
Copy link
Member Author

@pcattori has a couple more things to do and then after a thumbs up review we should be able to merge. Expecting to be done today.

@sciutand
Copy link

sciutand commented Jan 18, 2023

Will this solution address the issue #1555 where the dev server runs out of memory and crashes?.
Having issues here caused by current implementation of clearing the resolve cache on each request since it has well known memory leaks that cause the above.

@pcattori
Copy link
Contributor

@sciutand yes the new dev server doesn't do any require cache purging, so if that was the cause of your issue, that will should be gone once you opt-in to the new dev server

@pcattori pcattori moved this from In Progress to Merged in Roadmap Jan 18, 2023
@pcattori
Copy link
Contributor

Main PR is merged: #5133

Waiting on fixes to unrelated changes that are breaking the dev branch, and then I can merge in this quick fix: #5137

@scottcorgan
Copy link

@sciutand yes the new dev server doesn't do any require cache purging, so if that was the cause of your issue, that will should be gone once you opt-in to the new dev server

@pcattori How do you opt into the new dev server?

@izznat
Copy link
Contributor

izznat commented Jan 19, 2023

@scottcorgan Usually remix provide a feature flag for experimental feature that you can define in remix.config.js. They will let us know what flag to use when the feature is released.

@pcattori
Copy link
Contributor

@scottcorgan like @izznatsir said, we're providing a future flag (future.unstable_dev) that you can set in remix.config.js. See the changeset on the main PR for more details, though we'll include all of this in the release notes once its available.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
proposal:remix Proposals from the Remix team.
Projects
Archived in project
Development

No branches or pull requests

9 participants