Skip to content

Commit

Permalink
docs(v2_dev): instructions for using and migrating to v2_dev
Browse files Browse the repository at this point in the history
  • Loading branch information
pcattori committed Jun 15, 2023
1 parent 0e15115 commit 6b53573
Show file tree
Hide file tree
Showing 4 changed files with 353 additions and 3 deletions.
2 changes: 1 addition & 1 deletion docs/other-api/adapter.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "@remix-run/{adapter}"
order: 2
order: 3
---

# Server Adapters
Expand Down
260 changes: 260 additions & 0 deletions docs/other-api/dev-v2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
---
title: "@remix-run/dev CLI (v2)"
order: 2
new: true
---

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.

To get a full list of available commands and flags, run:

```sh
npx @remix-run/dev -h
```

## `remix build`

Builds your app for production. This command will set `process.env.NODE_ENV` to `production` and minify the output for deployment.

```sh
remix build
```

### Options

| Option | flag | config | default |
| ---------------------------------------- | ------------- | ------ | ------- |
| Generate sourcemaps for production build | `--sourcemap` | N/A | `false` |

## `remix dev`

Builds your app and spins up the Remix dev server alongside your app server.

The dev server will:

1. Set `NODE_ENV` to `development`
2. Watch your app code for changes and trigger rebuilds
3. Restart your app server whenever rebuilds succeed
4. Send code updates to the browser via Live Reload and HMR + Hot Data Revalidation

### With `remix-serve`

Enable the v2 dev server:

```js filename=remix.config.js
module.exports = {
future: {
v2_dev: true,
},
};
```

That's it!

### With custom app server

If you used a template to get started, hopefully it has integration with the v2 dev server out-of-the-box.
If not, you can follow these steps to integrate your project with `v2_dev`:

1. Enable the v2 dev server:

```js filename=remix.config.js
module.exports = {
future: {
v2_dev: true,
},
};
```

2. Replace your dev scripts in `package.json` and use `-c` to specify your app server command:

```json
{
"dev": "remix dev -c 'node ./server.js'"
}
```

3. Ensure `broadcastDevReady` is called when your app server is up and running:

```js filename=server.js lines=[12,25-27]
import path from "node:path";

import { broadcastDevReady } from "@remix-run/node";
import express from "express";

const BUILD_DIR = path.resolve(__dirname, "build");
const build = require(BUILD_DIR);

const app = express();

// ... code for setting up your express app goes here ...

app.all(
"*",
createRequestHandler({
build,
mode: process.env.NODE_ENV,
})
);

const port = 3000;
app.listen(port, () => {
console.log(`👉 http://localhost:${port}`);

if (process.env.NODE_ENV === "development") {
broadcastDevReady(build);
}
});
```

<docs-info>

For CloudFlare, use `logDevReady` instead of `broadcastDevReady`.

Why? `broadcastDevReady` uses `fetch` to send a ready message to the dev server,
but CloudFlare does not support async I/O like `fetch` outside of request handling.

</docs-info>

### Options

Options priority order is: 1. flags, 2. config, 3. defaults.

| Option | flag | config | default |
| --------------- | ------------------ | ---------------- | ------------------------------------------------- |
| Command | `-c` / `--command` | `command` | `remix-serve <server build path>` |
| No restart | `--no-restart` | `restart: false` | `restart: true` |
| Scheme | `--scheme` | `scheme` | `https` if TLS key/cert are set, otherwise `http` |
| Host | `--host` | `host` | `localhost` |
| Port | `--port` | `port` | Dynamically chosen open port |
| TLS key | `--tls-key` | `tlsKey` | N/A |
| TLS certificate | `--tls-cert` | `tlsCert` | N/A |

<docs-info>

The scheme/host/port options only affect the Remix dev server, and **do not affect your app server**.
Your app will run on your app server's normal URL.

You most likely won't want to configure the scheme/host/port for the dev server,
as those are implementation details used internally for hot updates.
They exist in case you need fine-grain control, for example Docker networking or using specific open ports.

</docs-info>

For example, to override the port used by the dev server via config:

```js filename=remix.config.js
module.exports = {
future: {
v2_dev: {
port: 8001,
},
},
};
```

### Keep app server running across rebuilds

By default, the Remix dev server restarts your app server when rebuilds occur.
This is a simple way to ensure that your app server is up-to-date with the latest code changes.

If you'd like to opt-out of this behavior use the `--no-restart` flag:

```sh
remix dev --no-restart -c 'node ./server.js'
```

🚨 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.

> With great power comes great responsibility.
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.

If you're using CJS but looking at an ESM template, you'll need to swap out `import` cache busting with `require` cache busting:

```diff
- const stat = fs.statSync(BUILD_DIR);
- build = import(BUILD_DIR + "?t=" + stat.mtimeMs);
+ for (const key in require.cache) {
+ if (key.startsWith(BUILD_DIR)) {
+ delete require.cache[key];
+ }
+ }
+ build = require(BUILD_DIR)
```

#### Pick up changes from other packages

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.

For example, you could have a UI library package (`packages/ui`) that is used within your Remix app (`packages/app`).
To pick up changes in `packages/ui`, you can configure [watchPaths][watch-paths] to include your packages.

#### Keep in-memory data and connections across rebuilds

Every time you re-import code to apply changes to your app server, that code will be run.
Rerunning each changed module works great in most cases, but sometimes you want to want to keep stuff around.

For example, it'd be nice if your app only connected to your database once and kept that connection around across rebuilds.
But since the connection is held in-memory, re-imports will wipe those out and cause your app to reconnect.

Luckily, there's a trick to get around this: use `global` as a cache for keeping things in-memory across rebuilds!
Here's a nifty utility adapted from [Jon Jensen's code][jenseng-code] for [his Remix Conf 2023 talk][jenseng-talk]:

```ts filename=app/utils/remember.ts
export function remember<T>(key: string, value: T) {
const g = global as any;
g.__singletons ??= {};
g.__singletons[key] ??= value;
return g.__singletons[key];
}
```

And here's how to use it to keep stuff around across rebuilds:

```ts filename=app/utils/db.server.ts
import { PrismaClient } from "@prisma/client";

import { remember } from "~/utils/remember";

// hard-code a unique key so we can look up the client when this module gets re-imported
export const db = remember("db", new PrismaClient());
```

### How to set up local HTTPS

For this example, let's use [mkcert][mkcert].
After you have it installed, make sure to:

- Create a local Certificate Authority if you haven't already done so
- Use `NODE_EXTRA_CA_CERTS` for Node compatibility

```sh
mkcert -install # create a local CA
export NODE_EXTRA_CA_CERTS="$(mkcert -CAROOT)/rootCA.pem" # tell Node to use our local CA
```

Now, create the TLS key and certificate:

```sh
mkcert -key-file key.pem -cert-file cert.pem localhost
```

👆 You can change `localhost` to something else if you are using custom hostnames.

Next, use the `key.pem` and `cert.pem` to get HTTPS working locally with your app server.
This depends on what you are using for your app server.
For example, here's how you could use HTTPS with an Express server:

```ts filename=server.js
import fs from "node:fs";
import https from "node:https";

import express from "express";

const app = express();

// ... code setting up your express app goes here ...

//
```
9 changes: 9 additions & 0 deletions docs/other-api/dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ title: "@remix-run/dev (CLI)"
order: 1
---

<docs-warning>

The Remix CLI is changing in v2.
You can prepare for this change at your convenience with the `v2_dev` future flag.
For instructions on making this change see the [v2 guide][v2-guide].

</docs-warning>

# Remix CLI

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.
Expand Down Expand Up @@ -113,3 +121,4 @@ Skip deleting the `remix.init` folder after initialization has been run. Useful
[remix-app-server]: ./serve
[node-inspector]: https://nodejs.org/en/docs/guides/debugging-getting-started
[templates-folder-of-the-remix-repository]: https://github.com/remix-run/remix/tree/main/templates
[v2-guide]: ../pages/v2
85 changes: 83 additions & 2 deletions docs/pages/v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -705,9 +705,88 @@ In your `remix.config.js`, you should specify either `serverModuleFormat: "cjs"`

## Dev Server

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.
For configuration options, see the [`remix dev` docs][v2-dev-config].

We expect to stabilize in Remix `v1.17.0`. Once that ships, we'll have more instructions here to prepare your app for v2.
### `remix-serve`

Enable the v2 dev server:

```js filename=remix.config.js
module.exports = {
future: {
v2_dev: true,
},
};
```

That's it!

### custom app server

Check out our [templates][templates] for examples of how to integrate with `v2_dev`
or follow these steps:

1. Enable the v2 dev server:

```js filename=remix.config.js
module.exports = {
future: {
v2_dev: true,
},
};
```

2. Update `scripts` in `package.json`:

- Replace any `remix watch` with `remix dev`
- Remove redundant `NODE_ENV=development`
- Use `-c` / `--command` to run your app server

For example:

```diff filename=package.json
{
"scripts": {
- "dev:remix": "cross-env NODE_ENV=development remix watch",
- "dev:server": "cross-env NODE_ENV=development node ./server.js"
+ "dev": "remix dev -c 'node ./server.js'",
}
}
```

3. Send a "ready" message to the dev server once your app is running

```ts filename=server.js lines=[1-2,11]
import { broadcastDevReady } from "@remix-run/node";
// import { logDevReady } from "@remix-run/cloudflare" // use `logDevReady` if using CloudFlare

const BUILD_DIR = path.join(process.cwd(), "build");

// ... code setting up your server goes here ...

const port = 3000;
app.listen(port, async () => {
console.log(`👉 http://localhost:${port}`);
broadcastDevReady(await import(BUILD_DIR));
});
```

4. (Optional) `--no-restart`

If you were relying on `require` cache purging, you can keep doing so by using the `--no-restart` flag:

```sh
remix dev --no-restart -c 'node ./server.js'
```

<docs-info>

If you are purging your `require` cache for every request, you can instead switch to only busting the cache when your app server rebuilds.
To do so, use a file watcher like `chokidar`.

</docs-info>

See our [templates][templates] and the [`remix dev`] docs for examples on how to do this.

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

0 comments on commit 6b53573

Please sign in to comment.