Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
AlemTuzlak committed Jul 11, 2023
2 parents 8931b86 + 8ac053a commit 2ff1cfb
Show file tree
Hide file tree
Showing 50 changed files with 590 additions and 207 deletions.
9 changes: 9 additions & 0 deletions .changeset/dry-items-deliver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@remix-run/dev": minor
"@remix-run/react": minor
"@remix-run/server-runtime": minor
---

improved networking options for v2_dev

deprecate the `--scheme` and `--host` options and replace them with the `REMIX_DEV_ORIGIN` environment variable
5 changes: 5 additions & 0 deletions .changeset/famous-games-film.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/dev": patch
---

ignore missing react-dom/client for react 17
6 changes: 6 additions & 0 deletions .changeset/hydrate-error-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@remix-run/react": patch
"@remix-run/server-runtime": patch
---

Support proper hydration of `Error` subclasses such as `ReferenceError`/`TypeError` in development mode
5 changes: 5 additions & 0 deletions .changeset/kind-coins-attack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/dev": patch
---

ignore errors when killing already dead processes
5 changes: 5 additions & 0 deletions .changeset/nine-islands-mate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/dev": patch
---

fix sourcemaps for v2_dev
13 changes: 13 additions & 0 deletions .changeset/red-bananas-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"@remix-run/dev": minor
---

Output esbuild metafiles for bundle analysis

Written to server build directory (`build/` by default):

- `metafile.css.json`
- `metafile.js.json` (browser JS)
- `metafile.server.json` (server JS)

Metafiles can be uploaded to https://esbuild.github.io/analyze/ for analysis.
5 changes: 5 additions & 0 deletions .changeset/stylesheet-re-prefetch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/react": patch
---

Avoid re-prefetching stylesheets for active routes during a revalidation
11 changes: 11 additions & 0 deletions .changeset/tasty-bats-turn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@remix-run/dev": patch
---

Do not clear screen when dev server starts

On some terminal emulators, "clearing" only scrolls the next line to the
top. on others, it erases the scrollback.

Instead, let users call `clear` themselves (`clear && remix dev`) if
they want to clear.
5 changes: 5 additions & 0 deletions .changeset/type-userouteloaderdata.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/react": patch
---

Add generic type for `useRouteLoaderData()`
1 change: 1 addition & 0 deletions contributors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -557,5 +557,6 @@
- amir-ziaei
- mrkhosravian
- tanerijun
- toufiqnuur
- ally1002
- AlemTuzlak
17 changes: 15 additions & 2 deletions docs/guides/envvars.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,24 @@ export async function loader() {

If you're using the `@remix-run/cloudflare-pages` adapter, env variables work a little differently. Since Cloudflare Pages are powered by Functions, you'll need to define your local environment variables in the [`.dev.vars`][dev-vars] file. It has the same syntax as `.env` example file mentioned above.

Then, in your `loader` functions, you can access environment variables directly on `context`:
Then, you can pass those through via `getLoadContext` in your server file:

```ts
export const onRequest = createPagesFunctionHandler({
build,
getLoadContext(context) {
// Hand-off Cloudflare ENV vars to the Remix `context` object
return { env: context.env };
},
mode: process.env.NODE_ENV,
});
```

And they'll be available via Remix's `context` in your `loader`/`action` functions:

```tsx
export const loader = async ({ context }: LoaderArgs) => {
console.log(context.SOME_SECRET);
console.log(context.env.SOME_SECRET);
};
```

Expand Down
69 changes: 48 additions & 21 deletions docs/other-api/dev-v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,24 +144,23 @@ but CloudFlare does not support async I/O like `fetch` outside of request handli

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 |
| Option | flag | config | default |
| --------------- | ------------------ | ---------------- | --------------------------------- |
| Command | `-c` / `--command` | `command` | `remix-serve <server build path>` |
| No restart | `--no-restart` | `restart: false` | `restart: true` |
| 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**.
The port option only affects the Remix dev server, and **does 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.
You probably don't want to configure the port for the dev server,
as it is an implementation detail used internally for hot updates.
The port option exists in case you need fine-grain networking control,
for example to setup Docker networking or use a specific open port for security purposes.

</docs-info>

Expand Down Expand Up @@ -256,9 +255,11 @@ To use [Mock Service Worker][msw] in development, you'll need to:
1. Run MSW as part of your app server
2. Configure MSW to not mock internal "dev ready" messages to the dev server

`remix dev` will provide the `REMIX_DEV_ORIGIN` environment variable for use in your app server.

For example, if you are using [binode][binode] to integrate with MSW,
make sure that the call to `binode` is within the `remix dev -c` subcommand.
That way, the MSW server will have access to the `REMIX_DEV_HTTP_ORIGIN` environment variable:
That way, the MSW server will have access to the `REMIX_DEV_ORIGIN` environment variable:

```json filename=package.json
{
Expand All @@ -269,16 +270,18 @@ That way, the MSW server will have access to the `REMIX_DEV_HTTP_ORIGIN` environ
}
```

Next, you can use `REMIX_DEV_HTTP_ORIGIN` to let MSW forward internal "dev ready" messages on `/ping`:
Next, you can use `REMIX_DEV_ORIGIN` to let MSW forward internal "dev ready" messages on `/ping`:

```ts
import { rest } from "msw";

const REMIX_DEV_PING = new URL(
process.env.REMIX_DEV_ORIGIN
);
REMIX_DEV_PING.pathname = "/ping";

export const server = setupServer(
rest.post(
`${process.env.REMIX_DEV_HTTP_ORIGIN}/ping`,
(req) => req.passthrough()
)
rest.post(REMIX_DEV_PING.href, (req) => req.passthrough())
// ... other request handlers go here ...
);
```
Expand Down Expand Up @@ -350,6 +353,31 @@ remix dev --tls-key=key.pem --tls-cert=cert.pem -c 'node ./server.js'
Alternatively, you can specify the TLS key and cert via the `v2_dev.tlsCert` and `v2_dev.tlsKey` config options.
Now your app server and dev server are TLS ready!

### How to integrate with a reverse proxy

Let's say you have the app server and dev server both running on the same machine:

- App server 👉 `http://localhost:1234`
- Dev server 👉 `http://localhost:5678`

Then, you setup a reverse proxy in front of the app server and dev server:

- Reverse proxy 👉 `https://myhost`

But the internal HTTP and WebSocket connections to support hot updates will still try to reach the dev server's unproxied origin:

- Hot updates 👉 `http://localhost:5678` / `ws://localhost:5678`

To get the internal connections to point to the reverse proxy, you can use the `REMIX_DEV_ORIGIN` environment variable:

```sh
REMIX_DEV_ORIGIN=https://myhost remix dev
```

Now, hot updates will be sent correctly to the proxy:

- Hot updates 👉 `https://myhost` / `wss://myhost`

### Troubleshooting

#### HMR: hot updates losing app state
Expand All @@ -358,8 +386,7 @@ Hot Module Replacement is supposed to keep your app's state around between hot u
But in some cases React cannot distinguish between existing components being changed and new components being added.
[React needs `key`s][react-keys] to disambiguate these cases and track changes when sibling elements are modified.

Additionally, when adding or removing hooks, React Refresh treats that as a brand-new component.
So if you add `useLoaderData` to your component, you may lose state local to that component.
Additionally, when adding or removing hooks, React Refresh treats that as a brand new component. So if you add `useLoaderData` to your component, you may lose state local to that component.

These are limitations of React and [React Refresh][react-refresh], not Remix.

Expand Down
2 changes: 1 addition & 1 deletion integration/abort-signal-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ test.beforeAll(async () => {
},
});

// This creates an interactive app using puppeteer.
// This creates an interactive app using playwright.
appFixture = await createAppFixture(fixture);
});

Expand Down
2 changes: 1 addition & 1 deletion integration/bug-report-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ test.beforeAll(async () => {
},
});

// This creates an interactive app using puppeteer.
// This creates an interactive app using playwright.
appFixture = await createAppFixture(fixture);
});

Expand Down
4 changes: 2 additions & 2 deletions integration/defer-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ test.describe("non-aborted", () => {
},
});

// This creates an interactive app using puppeteer.
// This creates an interactive app using playwright.
appFixture = await createAppFixture(fixture);
});

Expand Down Expand Up @@ -1244,7 +1244,7 @@ test.describe("aborted", () => {
},
});

// This creates an interactive app using puppeteer.
// This creates an interactive app using playwright.
appFixture = await createAppFixture(fixture);
});

Expand Down
44 changes: 43 additions & 1 deletion integration/error-sanitization-test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { test, expect } from "@playwright/test";
import { ServerMode } from "@remix-run/server-runtime/mode";

import { createFixture, js } from "./helpers/create-fixture";
import type { Fixture } from "./helpers/create-fixture";
import { createAppFixture, createFixture, js } from "./helpers/create-fixture";
import { PlaywrightFixture } from "./helpers/playwright-fixture";

const routeFiles = {
"app/root.jsx": js`
Expand Down Expand Up @@ -33,6 +34,10 @@ const routeFiles = {
if (new URL(request.url).searchParams.has('loader')) {
throw new Error("Loader Error");
}
if (new URL(request.url).searchParams.has('subclass')) {
// This will throw a ReferenceError
console.log(thisisnotathing);
}
return "LOADER"
}
Expand All @@ -58,6 +63,7 @@ const routeFiles = {
<>
<h1>Index Error</h1>
<p>{"MESSAGE:" + error.message}</p>
<p>{"NAME:" + error.name}</p>
{error.stack ? <p>{"STACK:" + error.stack}</p> : null}
</>
);
Expand Down Expand Up @@ -279,6 +285,21 @@ test.describe("Error Sanitization", () => {
);
expect(errorLogs[0][0].stack).toMatch(" at ");
});

test("does not support hydration of Error subclasses", async ({ page }) => {
let response = await fixture.requestDocument("/?subclass");
let html = await response.text();
expect(html).toMatch("<p>MESSAGE:Unexpected Server Error");
expect(html).toMatch("<p>NAME:Error");

// Hydration
let appFixture = await createAppFixture(fixture);
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/?subclass", true);
html = await app.getHtml();
expect(html).toMatch("<p>MESSAGE:Unexpected Server Error");
expect(html).toMatch("<p>NAME:Error");
});
});

test.describe("serverMode=development", () => {
Expand Down Expand Up @@ -428,6 +449,27 @@ test.describe("Error Sanitization", () => {
);
expect(errorLogs[0][0].stack).toMatch(" at ");
});

test("supports hydration of Error subclasses", async ({ page }) => {
let response = await fixture.requestDocument("/?subclass");
let html = await response.text();
expect(html).toMatch("<p>MESSAGE:thisisnotathing is not defined");
expect(html).toMatch("<p>NAME:ReferenceError");
expect(html).toMatch(
"<p>STACK:ReferenceError: thisisnotathing is not defined"
);

// Hydration
let appFixture = await createAppFixture(fixture, ServerMode.Development);
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/?subclass", true);
html = await app.getHtml();
expect(html).toMatch("<p>MESSAGE:thisisnotathing is not defined");
expect(html).toMatch("<p>NAME:ReferenceError");
expect(html).toMatch(
"STACK:ReferenceError: thisisnotathing is not defined"
);
});
});

test.describe("serverMode=production (user-provided handleError)", () => {
Expand Down
Loading

0 comments on commit 2ff1cfb

Please sign in to comment.