Skip to content

Commit 9aba797

Browse files
committed
docs(v2_dev): instructions for using and migrating to v2_dev
1 parent 82a9f98 commit 9aba797

File tree

4 files changed

+353
-3
lines changed

4 files changed

+353
-3
lines changed

docs/other-api/adapter.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: "@remix-run/{adapter}"
3-
order: 2
3+
order: 3
44
---
55

66
# Server Adapters

docs/other-api/dev-v2.md

+260
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
---
2+
title: "@remix-run/dev CLI (v2)"
3+
order: 2
4+
new: true
5+
---
6+
7+
The Remix CLI comes from the `@remix-run/dev` package. It also includes the compiler. Make sure it is in your `package.json` `devDependencies` so it doesn't get deployed to your server.
8+
9+
To get a full list of available commands and flags, run:
10+
11+
```sh
12+
npx @remix-run/dev -h
13+
```
14+
15+
## `remix build`
16+
17+
Builds your app for production. This command will set `process.env.NODE_ENV` to `production` and minify the output for deployment.
18+
19+
```sh
20+
remix build
21+
```
22+
23+
### Options
24+
25+
| Option | flag | config | default |
26+
| ---------------------------------------- | ------------- | ------ | ------- |
27+
| Generate sourcemaps for production build | `--sourcemap` | N/A | `false` |
28+
29+
## `remix dev`
30+
31+
Builds your app and spins up the Remix dev server alongside your app server.
32+
33+
The dev server will:
34+
35+
1. Set `NODE_ENV` to `development`
36+
2. Watch your app code for changes and trigger rebuilds
37+
3. Restart your app server whenever rebuilds succeed
38+
4. Send code updates to the browser via Live Reload and HMR + Hot Data Revalidation
39+
40+
### With `remix-serve`
41+
42+
Enable the v2 dev server:
43+
44+
```js filename=remix.config.js
45+
module.exports = {
46+
future: {
47+
v2_dev: true,
48+
},
49+
};
50+
```
51+
52+
That's it!
53+
54+
### With custom app server
55+
56+
If you used a template to get started, hopefully it has integration with the v2 dev server out-of-the-box.
57+
If not, you can follow these steps to integrate your project with `v2_dev`:
58+
59+
1. Enable the v2 dev server:
60+
61+
```js filename=remix.config.js
62+
module.exports = {
63+
future: {
64+
v2_dev: true,
65+
},
66+
};
67+
```
68+
69+
2. Replace your dev scripts in `package.json` and use `-c` to specify your app server command:
70+
71+
```json
72+
{
73+
"dev": "remix dev -c 'node ./server.js'"
74+
}
75+
```
76+
77+
3. Ensure `broadcastDevReady` is called when your app server is up and running:
78+
79+
```js filename=server.js lines=[12,25-27]
80+
import path from "node:path";
81+
82+
import { broadcastDevReady } from "@remix-run/node";
83+
import express from "express";
84+
85+
const BUILD_DIR = path.resolve(__dirname, "build");
86+
const build = require(BUILD_DIR);
87+
88+
const app = express();
89+
90+
// ... code for setting up your express app goes here ...
91+
92+
app.all(
93+
"*",
94+
createRequestHandler({
95+
build,
96+
mode: process.env.NODE_ENV,
97+
})
98+
);
99+
100+
const port = 3000;
101+
app.listen(port, () => {
102+
console.log(`👉 http://localhost:${port}`);
103+
104+
if (process.env.NODE_ENV === "development") {
105+
broadcastDevReady(build);
106+
}
107+
});
108+
```
109+
110+
<docs-info>
111+
112+
For CloudFlare, use `logDevReady` instead of `broadcastDevReady`.
113+
114+
Why? `broadcastDevReady` uses `fetch` to send a ready message to the dev server,
115+
but CloudFlare does not support async I/O like `fetch` outside of request handling.
116+
117+
</docs-info>
118+
119+
### Options
120+
121+
Options priority order is: 1. flags, 2. config, 3. defaults.
122+
123+
| Option | flag | config | default |
124+
| --------------- | ------------------ | ---------------- | ------------------------------------------------- |
125+
| Command | `-c` / `--command` | `command` | `remix-serve <server build path>` |
126+
| No restart | `--no-restart` | `restart: false` | `restart: true` |
127+
| Scheme | `--scheme` | `scheme` | `https` if TLS key/cert are set, otherwise `http` |
128+
| Host | `--host` | `host` | `localhost` |
129+
| Port | `--port` | `port` | Dynamically chosen open port |
130+
| TLS key | `--tls-key` | `tlsKey` | N/A |
131+
| TLS certificate | `--tls-cert` | `tlsCert` | N/A |
132+
133+
<docs-info>
134+
135+
The scheme/host/port options only affect the Remix dev server, and **do not affect your app server**.
136+
Your app will run on your app server's normal URL.
137+
138+
You most likely won't want to configure the scheme/host/port for the dev server,
139+
as those are implementation details used internally for hot updates.
140+
They exist in case you need fine-grain control, for example Docker networking or using specific open ports.
141+
142+
</docs-info>
143+
144+
For example, to override the port used by the dev server via config:
145+
146+
```js filename=remix.config.js
147+
module.exports = {
148+
future: {
149+
v2_dev: {
150+
port: 8001,
151+
},
152+
},
153+
};
154+
```
155+
156+
### Keep app server running across rebuilds
157+
158+
By default, the Remix dev server restarts your app server when rebuilds occur.
159+
This is a simple way to ensure that your app server is up-to-date with the latest code changes.
160+
161+
If you'd like to opt-out of this behavior use the `--no-restart` flag:
162+
163+
```sh
164+
remix dev --no-restart -c 'node ./server.js'
165+
```
166+
167+
🚨 BUT that means you are now on the hook for applying changes to your running app server _and_ telling the dev server when those changes have been applied.
168+
169+
> With great power comes great responsibility.
170+
171+
Check out our [templates][templates] for examples on how to use `import` cache busting to apply code changes to your app server while it keeps running.
172+
173+
If you're using CJS but looking at an ESM template, you'll need to swap out `import` cache busting with `require` cache busting:
174+
175+
```diff
176+
- const stat = fs.statSync(BUILD_DIR);
177+
- build = import(BUILD_DIR + "?t=" + stat.mtimeMs);
178+
+ for (const key in require.cache) {
179+
+ if (key.startsWith(BUILD_DIR)) {
180+
+ delete require.cache[key];
181+
+ }
182+
+ }
183+
+ build = require(BUILD_DIR)
184+
```
185+
186+
#### Pick up changes from other packages
187+
188+
If you are using a monorepo, you might want Remix to perform hot updates not only when your app code changes, but whenever you change code in any of your apps dependencies.
189+
190+
For example, you could have a UI library package (`packages/ui`) that is used within your Remix app (`packages/app`).
191+
To pick up changes in `packages/ui`, you can configure [watchPaths][watch-paths] to include your packages.
192+
193+
#### Keep in-memory data and connections across rebuilds
194+
195+
Every time you re-import code to apply changes to your app server, that code will be run.
196+
Rerunning each changed module works great in most cases, but sometimes you want to want to keep stuff around.
197+
198+
For example, it'd be nice if your app only connected to your database once and kept that connection around across rebuilds.
199+
But since the connection is held in-memory, re-imports will wipe those out and cause your app to reconnect.
200+
201+
Luckily, there's a trick to get around this: use `global` as a cache for keeping things in-memory across rebuilds!
202+
Here's a nifty utility adapted from [Jon Jensen's code][jenseng-code] for [his Remix Conf 2023 talk][jenseng-talk]:
203+
204+
```ts filename=app/utils/remember.ts
205+
export function remember<T>(key: string, value: T) {
206+
const g = global as any;
207+
g.__singletons ??= {};
208+
g.__singletons[key] ??= value;
209+
return g.__singletons[key];
210+
}
211+
```
212+
213+
And here's how to use it to keep stuff around across rebuilds:
214+
215+
```ts filename=app/utils/db.server.ts
216+
import { PrismaClient } from "@prisma/client";
217+
218+
import { remember } from "~/utils/remember";
219+
220+
// hard-code a unique key so we can look up the client when this module gets re-imported
221+
export const db = remember("db", new PrismaClient());
222+
```
223+
224+
### How to set up local HTTPS
225+
226+
For this example, let's use [mkcert][mkcert].
227+
After you have it installed, make sure to:
228+
229+
- Create a local Certificate Authority if you haven't already done so
230+
- Use `NODE_EXTRA_CA_CERTS` for Node compatibility
231+
232+
```sh
233+
mkcert -install # create a local CA
234+
export NODE_EXTRA_CA_CERTS="$(mkcert -CAROOT)/rootCA.pem" # tell Node to use our local CA
235+
```
236+
237+
Now, create the TLS key and certificate:
238+
239+
```sh
240+
mkcert -key-file key.pem -cert-file cert.pem localhost
241+
```
242+
243+
👆 You can change `localhost` to something else if you are using custom hostnames.
244+
245+
Next, use the `key.pem` and `cert.pem` to get HTTPS working locally with your app server.
246+
This depends on what you are using for your app server.
247+
For example, here's how you could use HTTPS with an Express server:
248+
249+
```ts filename=server.js
250+
import fs from "node:fs";
251+
import https from "node:https";
252+
253+
import express from "express";
254+
255+
const app = express();
256+
257+
// ... code setting up your express app goes here ...
258+
259+
//
260+
```

docs/other-api/dev.md

+9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ title: "@remix-run/dev (CLI)"
33
order: 1
44
---
55

6+
<docs-warning>
7+
8+
The Remix CLI is changing in v2.
9+
You can prepare for this change at your convenience with the `v2_dev` future flag.
10+
For instructions on making this change see the [v2 guide][v2-guide].
11+
12+
</docs-warning>
13+
614
# Remix CLI
715

816
The Remix CLI comes from the `@remix-run/dev` package. It also includes the compiler. Make sure it is in your `package.json` `devDependencies` so it doesn't get deployed to your server.
@@ -113,3 +121,4 @@ Skip deleting the `remix.init` folder after initialization has been run. Useful
113121
[remix-app-server]: ./serve
114122
[node-inspector]: https://nodejs.org/en/docs/guides/debugging-getting-started
115123
[templates-folder-of-the-remix-repository]: https://github.com/remix-run/remix/tree/main/templates
124+
[v2-guide]: ../pages/v2

docs/pages/v2.md

+83-2
Original file line numberDiff line numberDiff line change
@@ -705,9 +705,88 @@ In your `remix.config.js`, you should specify either `serverModuleFormat: "cjs"`
705705

706706
## Dev Server
707707

708-
We are still stabilizing the new dev server that enables HMR and simplifies integration with various servers. You can try it today with `unstable_dev` and referring to the [v1.16 Release Notes][v1-16-release-notes] to set it up.
708+
For configuration options, see the [`remix dev` docs][v2-dev-config].
709709

710-
We expect to stabilize in Remix `v1.17.0`. Once that ships, we'll have more instructions here to prepare your app for v2.
710+
### `remix-serve`
711+
712+
Enable the v2 dev server:
713+
714+
```js filename=remix.config.js
715+
module.exports = {
716+
future: {
717+
v2_dev: true,
718+
},
719+
};
720+
```
721+
722+
That's it!
723+
724+
### custom app server
725+
726+
Check out our [templates][templates] for examples of how to integrate with `v2_dev`
727+
or follow these steps:
728+
729+
1. Enable the v2 dev server:
730+
731+
```js filename=remix.config.js
732+
module.exports = {
733+
future: {
734+
v2_dev: true,
735+
},
736+
};
737+
```
738+
739+
2. Update `scripts` in `package.json`:
740+
741+
- Replace any `remix watch` with `remix dev`
742+
- Remove redundant `NODE_ENV=development`
743+
- Use `-c` / `--command` to run your app server
744+
745+
For example:
746+
747+
```diff filename=package.json
748+
{
749+
"scripts": {
750+
- "dev:remix": "cross-env NODE_ENV=development remix watch",
751+
- "dev:server": "cross-env NODE_ENV=development node ./server.js"
752+
+ "dev": "remix dev -c 'node ./server.js'",
753+
}
754+
}
755+
```
756+
757+
3. Send a "ready" message to the dev server once your app is running
758+
759+
```ts filename=server.js lines=[1-2,11]
760+
import { broadcastDevReady } from "@remix-run/node";
761+
// import { logDevReady } from "@remix-run/cloudflare" // use `logDevReady` if using CloudFlare
762+
763+
const BUILD_DIR = path.join(process.cwd(), "build");
764+
765+
// ... code setting up your server goes here ...
766+
767+
const port = 3000;
768+
app.listen(port, async () => {
769+
console.log(`👉 http://localhost:${port}`);
770+
broadcastDevReady(await import(BUILD_DIR));
771+
});
772+
```
773+
774+
4. (Optional) `--no-restart`
775+
776+
If you were relying on `require` cache purging, you can keep doing so by using the `--no-restart` flag:
777+
778+
```sh
779+
remix dev --no-restart -c 'node ./server.js'
780+
```
781+
782+
<docs-info>
783+
784+
If you are purging your `require` cache for every request, you can instead switch to only busting the cache when your app server rebuilds.
785+
To do so, use a file watcher like `chokidar`.
786+
787+
</docs-info>
788+
789+
See our [templates][templates] and the [`remix dev`] docs for examples on how to do this.
711790

712791
[future-flags]: ./api-development-strategy
713792
[remix-config]: ../file-conventions/remix-config
@@ -716,3 +795,5 @@ We expect to stabilize in Remix `v1.17.0`. Once that ships, we'll have more inst
716795
[meta-v2-rfc]: https://github.com/remix-run/remix/discussions/4462
717796
[meta-v2-matches]: #the-matches-argument
718797
[v1-16-release-notes]: https://github.com/remix-run/remix/releases/tag/remix%401.16.0
798+
[v2-dev-config]: ../other-api/dev-v2
799+
[templates]: https://github.com/remix-run/remix/tree/main/templates

0 commit comments

Comments
 (0)