Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(dev): reuse dev server port for websocket #6476

Merged
merged 8 commits into from
May 25, 2023
23 changes: 23 additions & 0 deletions .changeset/wicked-pandas-give.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
"@remix-run/dev": minor
"@remix-run/react": minor
"@remix-run/server-runtime": minor
---

Reuse dev server port for WebSocket (Live Reload,HMR,HDR)

As a result the `webSocketPort`/`--websocket-port` option has been obsoleted.
Additionally, scheme/host/port options for the dev server have been renamed.

Available options are:

| Option | flag | config | default |
| -------------- | ------------------ | ---------------- | --------------------------------- |
| Command | `-c` / `--command` | `command` | `remix-serve <server build path>` |
| Scheme | `--scheme` | `scheme` | `http` |
| Host | `--host` | `host` | `localhost` |
| Port | `--port` | `port` | Dynamically chosen open port |
| No restart | `--no-restart` | `restart: false` | `restart: true` |

Note that scheme/host/port options are for the _dev server_, not your app server.
You probably don't need to use scheme/host/port option if you aren't configuring networking (e.g. for Docker or SSL).
36 changes: 17 additions & 19 deletions integration/hmr-log-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,15 @@ import { createFixtureProject, css, js, json } from "./helpers/create-fixture";

test.setTimeout(120_000);

let fixture = (options: {
appServerPort: number;
httpPort: number;
webSocketPort: number;
}) => ({
let fixture = (options: { appPort: number; devPort: number }) => ({
files: {
"remix.config.js": js`
module.exports = {
serverModuleFormat: "cjs",
tailwind: true,
future: {
unstable_dev: {
httpPort: ${options.httpPort},
webSocketPort: ${options.webSocketPort},
port: ${options.devPort},
},
v2_routeConvention: true,
v2_errorBoundary: true,
Expand Down Expand Up @@ -80,7 +75,7 @@ let fixture = (options: {
})
);

let port = ${options.appServerPort};
let port = ${options.appPort};
app.listen(port, () => {
let build = require(BUILD_DIR);
console.log('✅ app ready: http://localhost:' + port);
Expand Down Expand Up @@ -250,12 +245,9 @@ test("HMR", async ({ page }) => {
});

let portRange = makeRange(4080, 4099);
let appServerPort = await getPort({ port: portRange });
let httpPort = await getPort({ port: portRange });
let webSocketPort = await getPort({ port: portRange });
let projectDir = await createFixtureProject(
fixture({ appServerPort, httpPort, webSocketPort })
);
let appPort = await getPort({ port: portRange });
let devPort = await getPort({ port: portRange });
let projectDir = await createFixtureProject(fixture({ appPort, devPort }));

// spin up dev server
let dev = execa("npm", ["run", "dev"], { cwd: projectDir });
Expand All @@ -270,7 +262,7 @@ test("HMR", async ({ page }) => {
{ timeoutMs: 10_000 }
);

await page.goto(`http://localhost:${appServerPort}`, {
await page.goto(`http://localhost:${appPort}`, {
waitUntil: "networkidle",
});

Expand Down Expand Up @@ -470,7 +462,7 @@ whatsup
await page.getByText("Hello, planet").waitFor({ timeout: HMR_TIMEOUT_MS });
await page.waitForLoadState("networkidle");

expect(devStderr()).toBe("");
let stderr = devStderr();
let withSyntaxError = `
import { useLoaderData } from "@remix-run/react";
export function shouldRevalidate(args) {
Expand All @@ -486,9 +478,15 @@ whatsup
}
`;
fs.writeFileSync(indexPath, withSyntaxError);
await wait(() => devStderr().includes('Expected ";" but found "efault"'), {
timeoutMs: HMR_TIMEOUT_MS,
});
await wait(
() =>
devStderr()
.replace(stderr, "")
.includes('Expected ";" but found "efault"'),
{
timeoutMs: HMR_TIMEOUT_MS,
}
);

let withFix = `
import { useLoaderData } from "@remix-run/react";
Expand Down
39 changes: 18 additions & 21 deletions integration/hmr-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,15 @@ import { createFixtureProject, css, js, json } from "./helpers/create-fixture";

test.setTimeout(120_000);

let fixture = (options: {
appServerPort: number;
httpPort: number;
webSocketPort: number;
}) => ({
let fixture = (options: { appPort: number; devPort: number }) => ({
files: {
"remix.config.js": js`
module.exports = {
serverModuleFormat: "cjs",
tailwind: true,
future: {
unstable_dev: {
httpPort: ${options.httpPort},
webSocketPort: ${options.webSocketPort},
port: ${options.devPort},
},
v2_routeConvention: true,
v2_errorBoundary: true,
Expand Down Expand Up @@ -69,18 +64,17 @@ let fixture = (options: {
const app = express();
app.use(express.static("public", { immutable: true, maxAge: "1y" }));

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

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

let port = ${options.appServerPort};
let port = ${options.appPort};
app.listen(port, () => {
let build = require(BUILD_DIR);
console.log('✅ app ready: http://localhost:' + port);
Expand Down Expand Up @@ -249,12 +243,9 @@ test("HMR", async ({ page }) => {
});

let portRange = makeRange(3080, 3099);
let appServerPort = await getPort({ port: portRange });
let httpPort = await getPort({ port: portRange });
let webSocketPort = await getPort({ port: portRange });
let projectDir = await createFixtureProject(
fixture({ appServerPort, httpPort, webSocketPort })
);
let appPort = await getPort({ port: portRange });
let devPort = await getPort({ port: portRange });
let projectDir = await createFixtureProject(fixture({ appPort, devPort }));

// spin up dev server
let dev = execa("npm", ["run", "dev"], { cwd: projectDir });
Expand All @@ -269,7 +260,7 @@ test("HMR", async ({ page }) => {
{ timeoutMs: 10_000 }
);

await page.goto(`http://localhost:${appServerPort}`, {
await page.goto(`http://localhost:${appPort}`, {
waitUntil: "networkidle",
});

Expand Down Expand Up @@ -469,7 +460,7 @@ whatsup
await page.getByText("Hello, planet").waitFor({ timeout: HMR_TIMEOUT_MS });
await page.waitForLoadState("networkidle");

expect(devStderr()).toBe("");
let stderr = devStderr();
let withSyntaxError = `
import { useLoaderData } from "@remix-run/react";
export function shouldRevalidate(args) {
Expand All @@ -485,9 +476,15 @@ whatsup
}
`;
fs.writeFileSync(indexPath, withSyntaxError);
await wait(() => devStderr().includes('Expected ";" but found "efault"'), {
timeoutMs: HMR_TIMEOUT_MS,
});
await wait(
() =>
devStderr()
.replace(stderr, "")
.includes('Expected ";" but found "efault"'),
{
timeoutMs: HMR_TIMEOUT_MS,
}
);

let withFix = `
import { useLoaderData } from "@remix-run/react";
Expand Down
7 changes: 3 additions & 4 deletions packages/remix-dev/__tests__/cli-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,10 @@ describe("remix CLI", () => {

[unstable_dev]
--command, -c Command used to run your app server
--http-scheme HTTP(S) scheme for the dev server. Default: http
--http-host HTTP(S) host for the dev server. Default: localhost
--http-port HTTP(S) port for the dev server. Default: any open port
--scheme Scheme for the dev server. Default: http
--host Host for the dev server. Default: localhost
--port Port for the dev server. Default: any open port
--no-restart Do not restart the app server when rebuilds occur.
--websocket-port WebSocket port for the dev server. Default: any open port
\`init\` Options:
--no-delete Skip deleting the \`remix.init\` script
\`routes\` Options:
Expand Down
78 changes: 30 additions & 48 deletions packages/remix-dev/cli/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,8 @@ export async function build(
onWarning: warnOnce,
};
if (mode === "development" && config.future.unstable_dev) {
let dev = await resolveDevBuild(config);
options.devHttpOrigin = {
scheme: dev.httpScheme,
host: dev.httpHost,
port: dev.httpPort,
};
options.devWebSocketPort = dev.webSocketPort;
let origin = await resolveDevOrigin(config);
options.devOrigin = origin;
}

fse.emptyDirSync(config.assetsBuildDirectory);
Expand Down Expand Up @@ -216,20 +211,20 @@ export async function dev(
remixRoot: string,
flags: {
debug?: boolean;
port?: number; // TODO: remove for v2

// unstable_dev
command?: string;
httpScheme?: string;
httpHost?: string;
httpPort?: number;
scheme?: string;
host?: string;
port?: number;
restart?: boolean;
websocketPort?: number;
} = {}
) {
if (process.env.NODE_ENV && process.env.NODE_ENV !== "development") {
console.warn(
`Forcing NODE_ENV to be 'development'. Was: ${process.env.NODE_ENV}`
`Forcing NODE_ENV to be 'development'. Was: ${JSON.stringify(
process.env.NODE_ENV
)}`
);
}
process.env.NODE_ENV = "development";
Expand Down Expand Up @@ -471,49 +466,42 @@ let parseMode = (

let findPort = async () => getPort({ port: makeRange(3001, 3100) });

type DevBuildFlags = {
httpScheme: string;
httpHost: string;
httpPort: number;
webSocketPort: number;
type DevOrigin = {
scheme: string;
host: string;
port: number;
};
let resolveDevBuild = async (
let resolveDevOrigin = async (
config: RemixConfig,
flags: Partial<DevBuildFlags> = {}
): Promise<DevBuildFlags> => {
flags: Partial<DevOrigin> = {}
): Promise<DevOrigin> => {
let dev = config.future.unstable_dev;
if (dev === false) throw Error("This should never happen");

// prettier-ignore
let httpScheme =
flags.httpScheme ??
(dev === true ? undefined : dev.httpScheme) ??
let scheme =
flags.scheme ??
(dev === true ? undefined : dev.scheme) ??
"http";
// prettier-ignore
let httpHost =
flags.httpHost ??
(dev === true ? undefined : dev.httpHost) ??
let host =
flags.host ??
(dev === true ? undefined : dev.host) ??
"localhost";
// prettier-ignore
let httpPort =
flags.httpPort ??
(dev === true ? undefined : dev.httpPort) ??
(await findPort());
// prettier-ignore
let webSocketPort =
flags.webSocketPort ??
(dev === true ? undefined : dev.webSocketPort) ??
let port =
flags.port ??
(dev === true ? undefined : dev.port) ??
(await findPort());

return {
httpScheme,
httpHost,
httpPort,
webSocketPort,
scheme,
host,
port,
};
};

type DevServeFlags = DevBuildFlags & {
type DevServeFlags = DevOrigin & {
command: string;
restart: boolean;
};
Expand All @@ -524,10 +512,7 @@ let resolveDevServe = async (
let dev = config.future.unstable_dev;
if (dev === false) throw Error("Cannot resolve dev options");

let { httpScheme, httpHost, httpPort, webSocketPort } = await resolveDevBuild(
config,
flags
);
let origin = await resolveDevOrigin(config, flags);

// prettier-ignore
let command =
Expand Down Expand Up @@ -558,10 +543,7 @@ let resolveDevServe = async (

return {
command,
httpScheme,
httpHost,
httpPort,
webSocketPort,
...origin,
restart,
};
};
Loading