Skip to content

Commit 25570de

Browse files
committed
Merge branch 'dev' into brophdawg11/no-default-export
2 parents 1132d8f + 1f29147 commit 25570de

File tree

10 files changed

+576
-18
lines changed

10 files changed

+576
-18
lines changed

.changeset/fix-express-proxy.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@remix-run/express": patch
3+
---
4+
5+
Allow the `@remix-run/express` adapter to work behind a proxy when using `app.enable('trust proxy')`
6+
7+
- Previously, this used `req.get('host')` to construct the Remix `Request`, but that does not respect `X-Forwarded-Host`
8+
- This now uses `req.hostname` which will respect `X-Forwarded-Host`

docs/future/vite.md

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ toc: false
1414
| Feature | Node | Deno | Cloudflare | Notes |
1515
| ---------------------------- | ---- | ---- | ---------- | --------------------------------------------------------------------- |
1616
| Built-in dev server |||| |
17-
| Other servers (e.g. Express) | | || |
17+
| Other servers (e.g. Express) | | || |
1818
| HMR |||| |
1919
| HDR |||| |
2020
| MDX routes |||| [Supported with some deprecations.][supported-with-some-deprecations] |
@@ -494,10 +494,92 @@ If you want to reuse values across routes, stick them in their own non-route mod
494494
export const myValue = "some value";
495495
```
496496

497-
#### Adding and Removing Hooks
497+
#### Changing Hooks
498498

499499
React Fast Refresh cannot track changes for a component when hooks are being added or removed from it, causing full reloads just for the next render. After the hooks have been updated, changes should result in hot updates again. For example, if you add [`useLoaderData`][use_loader_data] to your component, you may lose state local to that component for that render.
500500

501+
Additionally, if you are destructuring a hook's return value, React Fast Refresh will not be able to preserve state for the component if the destructured key is removed or renamed.
502+
For example:
503+
504+
```tsx
505+
export const loader = () => {
506+
return json({ stuff: "some things" });
507+
};
508+
509+
export default function Component() {
510+
const { stuff } = useLoaderData<typeof loader>();
511+
return (
512+
<div>
513+
<input />
514+
<p>{stuff}</p>
515+
</div>
516+
);
517+
}
518+
```
519+
520+
If you change the key `stuff` to `things`:
521+
522+
```diff
523+
export const loader = () => {
524+
- return json({ stuff: "some things" })
525+
+ return json({ things: "some things" })
526+
}
527+
528+
export default Component() {
529+
- let { stuff } = useLoaderData<typeof loader>()
530+
+ let { things } = useLoaderData<typeof loader>()
531+
return (
532+
<div>
533+
<input />
534+
- <p>{stuff}</p>
535+
+ <p>{things}</p>
536+
</div>
537+
)
538+
}
539+
```
540+
541+
then React Fast Refresh will not be able to preserve state `<input />` ❌.
542+
543+
As a workaround, you could refrain from destructuring and instead use the hook's return value directly:
544+
545+
```tsx
546+
export const loader = () => {
547+
return json({ stuff: "some things" });
548+
};
549+
550+
export default function Component() {
551+
const data = useLoaderData<typeof loader>();
552+
return (
553+
<div>
554+
<input />
555+
<p>{data.stuff}</p>
556+
</div>
557+
);
558+
}
559+
```
560+
561+
Now if you change the key `stuff` to `things`:
562+
563+
```diff
564+
export const loader = () => {
565+
- return json({ things: "some things" })
566+
+ return json({ things: "some things" })
567+
}
568+
569+
export default Component() {
570+
let data = useLoaderData<typeof loader>()
571+
return (
572+
<div>
573+
<input />
574+
- <p>{data.stuff}</p>
575+
+ <p>{data.things}</p>
576+
</div>
577+
)
578+
}
579+
```
580+
581+
then React Fast Refresh will preserve state for the `<input />`, though you may need to use [component keys][component-keys] as described in the next section if the stateful element (e.g. `<input />`) is a sibling of the changed element.
582+
501583
#### Component Keys
502584

503585
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.
@@ -560,3 +642,4 @@ We're definitely late to the Vite party, but we're excited to be here now!
560642
[sveltekit]: https://kit.svelte.dev/
561643
[supported-with-some-deprecations]: #mdx
562644
[rfr-preamble]: https://github.com/facebook/react/issues/16604#issuecomment-528663101
645+
[component-keys]: #component-keys

integration/helpers/dev.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { spawn } from "node:child_process";
2+
import type { Readable } from "node:stream";
3+
import execa from "execa";
4+
import getPort from "get-port";
5+
import resolveBin from "resolve-bin";
6+
import waitOn from "wait-on";
7+
8+
const isWindows = process.platform === "win32";
9+
10+
export async function viteDev(
11+
projectDir: string,
12+
options: { port?: number } = {}
13+
) {
14+
let viteBin = resolveBin.sync("vite");
15+
return node(projectDir, [viteBin, "dev"], options);
16+
}
17+
18+
export async function node(
19+
projectDir: string,
20+
command: string[],
21+
options: { port?: number } = {}
22+
) {
23+
let nodeBin = process.argv[0];
24+
let proc = spawn(nodeBin, command, {
25+
cwd: projectDir,
26+
env: process.env,
27+
stdio: "pipe",
28+
});
29+
let devStdout = bufferize(proc.stdout);
30+
let devStderr = bufferize(proc.stderr);
31+
32+
let port = options.port ?? (await getPort());
33+
await waitOn({
34+
resources: [`http://localhost:${port}/`],
35+
timeout: 10000,
36+
}).catch((err) => {
37+
let stdout = devStdout();
38+
let stderr = devStderr();
39+
throw new Error(
40+
[
41+
err.message,
42+
"",
43+
"exit code: " + proc.exitCode,
44+
"stdout: " + stdout ? `\n${stdout}\n` : "<empty>",
45+
"stderr: " + stderr ? `\n${stderr}\n` : "<empty>",
46+
].join("\n")
47+
);
48+
});
49+
50+
return { pid: proc.pid!, port: port };
51+
}
52+
53+
export async function kill(pid: number) {
54+
if (!isAlive(pid)) return;
55+
if (isWindows) {
56+
await execa("taskkill", ["/F", "/PID", pid.toString()]).catch((error) => {
57+
// taskkill 128 -> the process is already dead
58+
if (error.exitCode === 128) return;
59+
if (/There is no running instance of the task./.test(error.message))
60+
return;
61+
console.warn(error.message);
62+
});
63+
return;
64+
}
65+
await execa("kill", ["-9", pid.toString()]).catch((error) => {
66+
// process is already dead
67+
if (/No such process/.test(error.message)) return;
68+
console.warn(error.message);
69+
});
70+
}
71+
72+
// utils ------------------------------------------------------------
73+
74+
function bufferize(stream: Readable): () => string {
75+
let buffer = "";
76+
stream.on("data", (data) => (buffer += data.toString()));
77+
return () => buffer;
78+
}
79+
80+
function isAlive(pid: number) {
81+
try {
82+
process.kill(pid, 0);
83+
return true;
84+
} catch (error) {
85+
return false;
86+
}
87+
}

0 commit comments

Comments
 (0)