Skip to content

Commit

Permalink
Merge branch 'dev' into link-meta-short-circuit
Browse files Browse the repository at this point in the history
  • Loading branch information
jacob-ebey authored Apr 25, 2023
2 parents ce6355d + 6e66529 commit a6a5b3f
Show file tree
Hide file tree
Showing 106 changed files with 1,730 additions and 1,326 deletions.
5 changes: 5 additions & 0 deletions .changeset/cjs-esm-warning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/dev": patch
---

add warning for v2 "cjs"->"esm" serverModuleFormat default change
157 changes: 157 additions & 0 deletions .changeset/dev-server.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
---
"@remix-run/dev": minor
"@remix-run/server-runtime": minor
---

Dev server improvements

- Push-based app server syncing that doesn't rely on polling
- App server as a managed subprocess
- Gracefully handle new files and routes without crashing
- Statically serve static assets to avoid fetch errors during app server reboots

# Guide

## 1. Enable new dev server

Enable `unstable_dev` in `remix.config.js`:

```js
{
future: {
"unstable_dev": true
}
}
```

## 2. Update `package.json` scripts

Specify the command to run your app server with the `-c`/`--command` flag:

For Remix app server:

```json
{
"scripts": {
"dev": "NODE_ENV=development remix dev -c 'node_modules/.bin/remix-serve build'"
}
}
```

For any other servers, specify the command you use to run your production server.

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

## 3. Call `devReady` in your app server

For example, in an Express server:

```js
// server.mjs
import path from "node:path";

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

let BUILD_DIR = path.join(process.cwd(), "build"); // path to Remix's server build directory (`build/` by default)

let app = express();

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

app.listen(3000, () => {
let build = require(BUILD_DIR);
console.log("Ready: http://localhost:" + port);

// in development, call `devReady` _after_ your server is ready
if (process.env.NODE_ENV === "development") {
devReady(build);
}
});
```

## 4. That's it!

You should now be able to run the Remix Dev server:

```sh
$ npm run dev
# Ready: http://localhost:3000
```

Make sure you navigate to your app server's URL in the browser, not the dev server's URL.
In this example, that would be `http://localhost:3000`.

# Configuration

Most users won't need to configure the dev server, but you might need to if:

- You are setting up custom origins for SSL support or for Docker networking
- You want to handle server updates yourself (e.g. via require cache purging)

Example:

```js
{
future: {
unstable_dev: {
// HTTP(S) scheme used when sending `devReady` messages to the dev server
httpScheme: "https", // default: `"http"`
// HTTP(S) host used when sending `devReady` messages to the dev server
httpHost: "mycustomhost", // default: `"localhost"`
// HTTP(S) port internally used by the dev server to statically serve built assets and to receive app server `devReady` messages
httpPort: 8001, // default: Remix chooses an open port in the range 3001-3099
// Websocket port internally used by the dev server for sending updates to the browser (Live reload, HMR, HDR)
websocketPort: 8002, // default: Remix chooses an open port in the range 3001-3099
// Whether the app server should be restarted when app is rebuilt
// See `Advanced > restart` for more
restart: false, // default: `true`
}
}
}
```

You can also configure via flags. For example:

```sh
remix dev -c 'node ./server.mjs' --http-port=3001 --websocket-port=3002 --no-restart
```

See `remix dev --help` for more details.

### restart

If you want to manage app server updates yourself, you can use the `--no-restart` flag so that the Remix dev server doesn't restart the app server subprocess when a rebuild succeeds.

For example, if you rely on require cache purging to keep your app server running while server changes are pulled in, then you'll want to use `--no-restart`.

🚨 It is then your responsibility to call `devReady` whenever server changes are incorporated in your app server. 🚨

So for require cache purging, you'd want to:

1. Purge the require cache
2. `require` your server build
3. Call `devReady` within a `if (process.env.NODE_ENV === 'development')`

([Looking at you, Kent](https://github.com/kentcdodds/kentcdodds.com/blob/main/server/index.ts#L298) 😆)

---

The ultimate solution for `--no-restart` would be for you to implement _server-side_ HMR for your app server.
Note: server-side HMR is not to be confused with the client-side HMR provided by Remix.
Then your app server could continuously update itself with new build with 0 downtime and without losing in-memory data that wasn't affected by the server changes.

This is left as an exercise to the reader.
5 changes: 5 additions & 0 deletions .changeset/hdr-revalidate-only-when-needed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/react": patch
---

Revalidate loaders only when a change to one is detected.
6 changes: 6 additions & 0 deletions .changeset/install-globals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@remix-run/node": patch
"@remix-run/serve": patch
---

add `@remix-run/node/install` side-effect to allow `node --require @remix-run/node/install`
5 changes: 5 additions & 0 deletions .changeset/resource-route-boundary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/react": patch
---

Fix false-positive resource route identification if a route only exports a boundary
5 changes: 5 additions & 0 deletions .changeset/splat-index-matching.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/dev": patch
---

Fix a bug in route matching that wass preventing a single splat (`$.jsx`) route from matching a root `/` path
9 changes: 5 additions & 4 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@ jobs:
node-version-file: ".nvmrc"
cache: "yarn"

- name: 📥 Install deps
run: yarn --frozen-lockfile

- name: Disable Eslint GitHub Actions Annotations
- name: Disable GitHub Actions Annotations
run: |
echo "::remove-matcher owner=tsc::"
echo "::remove-matcher owner=eslint-compact::"
echo "::remove-matcher owner=eslint-stylish::"
- name: 📥 Install deps
run: yarn --frozen-lockfile

- name: 🔬 Lint
run: yarn lint

Expand Down
30 changes: 30 additions & 0 deletions .github/workflows/reusable-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ jobs:
node-version-file: ".nvmrc"
cache: "yarn"

- name: Disable GitHub Actions Annotations
run: |
echo "::remove-matcher owner=tsc::"
echo "::remove-matcher owner=eslint-compact::"
echo "::remove-matcher owner=eslint-stylish::"
- name: 📥 Install deps
run: yarn --frozen-lockfile

Expand Down Expand Up @@ -61,6 +67,12 @@ jobs:
node-version: ${{ matrix.node }}
cache: "yarn"

- name: Disable GitHub Actions Annotations
run: |
echo "::remove-matcher owner=tsc::"
echo "::remove-matcher owner=eslint-compact::"
echo "::remove-matcher owner=eslint-stylish::"
- name: 📥 Install deps
run: yarn --frozen-lockfile

Expand Down Expand Up @@ -93,6 +105,12 @@ jobs:
node-version: ${{ matrix.node }}
cache: "yarn"

- name: Disable GitHub Actions Annotations
run: |
echo "::remove-matcher owner=tsc::"
echo "::remove-matcher owner=eslint-compact::"
echo "::remove-matcher owner=eslint-stylish::"
- name: 📥 Install deps
run: yarn --frozen-lockfile

Expand Down Expand Up @@ -124,6 +142,12 @@ jobs:
node-version: ${{ matrix.node }}
cache: "yarn"

- name: Disable GitHub Actions Annotations
run: |
echo "::remove-matcher owner=tsc::"
echo "::remove-matcher owner=eslint-compact::"
echo "::remove-matcher owner=eslint-stylish::"
- name: 📥 Install deps
run: yarn --frozen-lockfile

Expand Down Expand Up @@ -155,6 +179,12 @@ jobs:
node-version: ${{ matrix.node }}
cache: "yarn"

- name: Disable GitHub Actions Annotations
run: |
echo "::remove-matcher owner=tsc::"
echo "::remove-matcher owner=eslint-compact::"
echo "::remove-matcher owner=eslint-stylish::"
- name: 📥 Install deps
run: yarn --frozen-lockfile

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ jobs:
if: github.repository == 'remix-run/remix'
uses: ./.github/workflows/reusable-test.yml
with:
node_version: '["19"]'
node_version: '["latest"]'
2 changes: 2 additions & 0 deletions contributors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
- ascorbic
- ashleyryan
- ashocean
- atesgoral
- athongsavath
- AustinGil
- awthwathje
Expand Down Expand Up @@ -513,3 +514,4 @@
- zhe
- arifqys
- iamzee
- TimonVS
6 changes: 6 additions & 0 deletions docs/pages/v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,12 @@ module.exports = {
};
```

## `serverModuleFormat`

The default server module output format will be changing from `cjs` to `esm`.

In your `remix.config.js`, you should specify either `serverModuleFormat: "cjs"` to retain existing behavior, or `serverModuleFormat: "esm"`, to opt into the future behavior.

## Dev Server

We are still stabilizing the new dev server that enables HMR, several CSS libraries (CSS Modules, Vanilla Extract, Tailwind, PostCSS) and simplifies integration with various servers.
Expand Down
14 changes: 5 additions & 9 deletions integration/error-data-request-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,17 +129,13 @@ test.describe("ErrorBoundary", () => {
});

test("returns a 405 x-remix-error on a data fetch with a bad method", async () => {
let response = await fixture.requestData(
`/loader-return-json`,
"routes/loader-return-json",
{
expect(() =>
fixture.requestData("/loader-return-json", "routes/loader-return-json", {
method: "TRACE",
}
})
).rejects.toThrowError(
`Failed to construct 'Request': 'TRACE' HTTP method is unsupported.`
);
expect(response.status).toBe(405);
expect(response.headers.get("X-Remix-Error")).toBe("yes");
expect(await response.text()).toMatch("Unexpected Server Error");
assertConsoleError('Error: Invalid request method "TRACE"');
});

test("returns a 403 x-remix-error on a data fetch GET to a bad path", async () => {
Expand Down
Loading

0 comments on commit a6a5b3f

Please sign in to comment.