Skip to content

Commit 2331aa7

Browse files
authored
fix(dev): statically serve built assets from dev server (#6173)
1 parent 13a3501 commit 2331aa7

File tree

8 files changed

+125
-39
lines changed

8 files changed

+125
-39
lines changed

packages/remix-dev/__tests__/cli-test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,14 @@ describe("remix CLI", () => {
115115
\`dev\` Options:
116116
--debug Attach Node.js inspector
117117
--port, -p Choose the port from which to run your app
118+
119+
[unstable_dev]
120+
--command, -c Command used to run your app server
121+
--http-scheme HTTP(S) scheme for the dev server. Default: http
122+
--http-host HTTP(S) host for the dev server. Default: localhost
123+
--http-port HTTP(S) port for the dev server. Default: any open port
124+
--no-restart Do not restart the app server when rebuilds occur.
125+
--websocket-port Websocket port for the dev server. Default: any open port
118126
\`init\` Options:
119127
--no-delete Skip deleting the \`remix.init\` script
120128
\`routes\` Options:

packages/remix-dev/cli/commands.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ export async function build(
177177
};
178178
if (config.future.unstable_dev) {
179179
let dev = await resolveDev(config.future.unstable_dev);
180-
options.devHttpPort = dev.httpPort;
180+
options.devHttpOrigin = dev.httpOrigin;
181181
options.devWebsocketPort = dev.websocketPort;
182182
}
183183

@@ -212,7 +212,11 @@ export async function dev(
212212
flags: {
213213
debug?: boolean;
214214
port?: number; // TODO: remove for v2
215+
216+
// unstable_dev
215217
command?: string;
218+
httpScheme?: string;
219+
httpHost?: string;
216220
httpPort?: number;
217221
restart?: boolean;
218222
websocketPort?: number;
@@ -466,17 +470,27 @@ let resolveDev = async (
466470
dev: Exclude<RemixConfig["future"]["unstable_dev"], false>,
467471
flags: {
468472
command?: string;
473+
httpScheme?: string;
474+
httpHost?: string;
469475
httpPort?: number;
470476
restart?: boolean;
471477
websocketPort?: number;
472478
} = {}
473479
): Promise<{
474480
command?: string;
475-
httpPort: number;
481+
httpOrigin: {
482+
scheme: string;
483+
host: string;
484+
port: number;
485+
};
476486
restart: boolean;
477487
websocketPort: number;
478488
}> => {
479489
let command = flags.command ?? (dev === true ? undefined : dev.command);
490+
let httpScheme =
491+
flags.httpScheme ?? (dev === true ? undefined : dev.httpScheme) ?? "http";
492+
let httpHost =
493+
flags.httpHost ?? (dev === true ? undefined : dev.httpHost) ?? "localhost";
480494
let httpPort =
481495
flags.httpPort ??
482496
(dev === true ? undefined : dev.httpPort) ??
@@ -490,7 +504,11 @@ let resolveDev = async (
490504

491505
return {
492506
command,
493-
httpPort,
507+
httpOrigin: {
508+
scheme: httpScheme,
509+
host: httpHost,
510+
port: httpPort,
511+
},
494512
websocketPort,
495513
restart,
496514
};

packages/remix-dev/cli/run.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@ ${colors.logoBlue("R")} ${colors.logoGreen("E")} ${colors.logoYellow(
4141
\`dev\` Options:
4242
--debug Attach Node.js inspector
4343
--port, -p Choose the port from which to run your app
44+
45+
[unstable_dev]
46+
--command, -c Command used to run your app server
47+
--http-scheme HTTP(S) scheme for the dev server. Default: http
48+
--http-host HTTP(S) host for the dev server. Default: localhost
49+
--http-port HTTP(S) port for the dev server. Default: any open port
50+
--no-restart Do not restart the app server when rebuilds occur.
51+
--websocket-port Websocket port for the dev server. Default: any open port
4452
\`init\` Options:
4553
--no-delete Skip deleting the \`remix.init\` script
4654
\`routes\` Options:
@@ -151,14 +159,11 @@ export async function run(argv: string[] = process.argv.slice(2)) {
151159

152160
let args = arg(
153161
{
154-
"--command": String,
155-
"-c": "--command",
156162
"--debug": Boolean,
157163
"--no-delete": Boolean,
158164
"--dry": Boolean,
159165
"--force": Boolean,
160166
"--help": Boolean,
161-
"--http-port": Number,
162167
"-h": "--help",
163168
"--install": Boolean,
164169
"--no-install": Boolean,
@@ -168,15 +173,21 @@ export async function run(argv: string[] = process.argv.slice(2)) {
168173
"--port": Number,
169174
"-p": "--port",
170175
"--remix-version": String,
171-
"--restart": Boolean,
172-
"--no-restart": Boolean,
173176
"--sourcemap": Boolean,
174177
"--template": String,
175178
"--token": String,
176179
"--typescript": Boolean,
177180
"--no-typescript": Boolean,
178181
"--version": Boolean,
179182
"-v": "--version",
183+
184+
// dev server
185+
"--command": String,
186+
"-c": "--command",
187+
"--http-scheme": String,
188+
"--http-host": String,
189+
"--http-port": Number,
190+
"--no-restart": Boolean,
180191
"--websocket-port": Number,
181192
},
182193
{
@@ -202,6 +213,14 @@ export async function run(argv: string[] = process.argv.slice(2)) {
202213
return;
203214
}
204215

216+
if (flags["http-scheme"]) {
217+
flags.httpScheme = flags["http-scheme"];
218+
delete flags["http-scheme"];
219+
}
220+
if (flags["http-host"]) {
221+
flags.httpHost = flags["http-host"];
222+
delete flags["http-host"];
223+
}
205224
if (flags["http-port"]) {
206225
flags.httpPort = flags["http-port"];
207226
delete flags["http-port"];

packages/remix-dev/compiler/options.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ export type Options = {
66
onWarning?: (message: string, key: string) => void;
77

88
// TODO: required in v2
9-
devHttpPort?: number;
9+
devHttpOrigin?: {
10+
scheme: string;
11+
host: string;
12+
port: number;
13+
};
1014
devWebsocketPort?: number;
1115
};

packages/remix-dev/compiler/server/compiler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ const createEsbuildConfig = (
102102
"process.env.REMIX_DEV_SERVER_WS_PORT": JSON.stringify(
103103
ctx.config.devServerPort
104104
),
105-
"process.env.REMIX_DEV_HTTP_PORT": JSON.stringify(
106-
ctx.options.devHttpPort ?? ""
105+
"process.env.REMIX_DEV_HTTP_ORIGIN": JSON.stringify(
106+
ctx.options.devHttpOrigin ?? "" // TODO: remove nullish check in v2
107107
),
108108
},
109109
jsx: "automatic",

packages/remix-dev/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ type Dev = {
4141
port?: number; // TODO: remove in v2
4242

4343
command?: string;
44+
httpScheme?: string;
45+
httpHost?: string;
4446
httpPort?: number;
4547
websocketPort?: number;
4648
restart?: boolean;

packages/remix-dev/devServer_unstable/index.ts

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,43 @@ import express from "express";
77
import * as Channel from "../channel";
88
import { type Manifest } from "../manifest";
99
import * as Compiler from "../compiler";
10-
import { type RemixConfig } from "../config";
10+
import { readConfig, type RemixConfig } from "../config";
1111
import { loadEnv } from "./env";
1212
import * as Socket from "./socket";
1313
import * as HMR from "./hmr";
1414
import { warnOnce } from "../warnOnce";
1515
import { detectPackageManager } from "../cli/detectPackageManager";
1616

17+
type Origin = {
18+
scheme: string;
19+
host: string;
20+
port: number;
21+
};
22+
23+
let stringifyOrigin = (o: Origin) => `${o.scheme}://${o.host}:${o.port}`;
24+
25+
let patchPublicPath = (
26+
config: RemixConfig,
27+
devHttpOrigin: Origin
28+
): RemixConfig => {
29+
// set public path to point to dev server
30+
// so that browser asks the dev server for assets
31+
return {
32+
...config,
33+
// dev server has its own origin, to `/build/` path will not cause conflicts with app server routes
34+
publicPath: stringifyOrigin(devHttpOrigin) + "/build/",
35+
};
36+
};
37+
1738
export let serve = async (
1839
initialConfig: RemixConfig,
1940
options: {
2041
command?: string;
21-
httpPort: number;
42+
httpOrigin: {
43+
scheme: string;
44+
host: string;
45+
port: number;
46+
};
2247
websocketPort: number;
2348
restart: boolean;
2449
}
@@ -42,23 +67,27 @@ export let serve = async (
4267
env: {
4368
NODE_ENV: "development",
4469
PATH: `${bin}:${process.env.PATH}`,
45-
REMIX_DEV_HTTP_PORT: String(options.httpPort),
70+
REMIX_DEV_HTTP_ORIGIN: stringifyOrigin(options.httpOrigin),
4671
},
4772
});
4873
};
4974

5075
let dispose = await Compiler.watch(
5176
{
52-
config: initialConfig,
77+
config: patchPublicPath(initialConfig, options.httpOrigin),
5378
options: {
5479
mode: "development",
5580
sourcemap: true,
5681
onWarning: warnOnce,
57-
devHttpPort: options.httpPort,
82+
devHttpOrigin: options.httpOrigin,
5883
devWebsocketPort: options.websocketPort,
5984
},
6085
},
6186
{
87+
reloadConfig: async (root) => {
88+
let config = await readConfig(root);
89+
return patchPublicPath(config, options.httpOrigin);
90+
},
6291
onBuildStart: (ctx) => {
6392
state.buildHashChannel?.err();
6493
clean(ctx.config);
@@ -110,6 +139,20 @@ export let serve = async (
110139
);
111140

112141
let httpServer = express()
142+
// statically serve built assets
143+
.use((_, res, next) => {
144+
res.header("Access-Control-Allow-Origin", "*");
145+
next();
146+
})
147+
.use(
148+
"/build",
149+
express.static(initialConfig.assetsBuildDirectory, {
150+
immutable: true,
151+
maxAge: "1y",
152+
})
153+
)
154+
155+
// handle `devReady` messages
113156
.use(express.json())
114157
.post("/ping", (req, res) => {
115158
let { buildHash } = req.body;
@@ -122,8 +165,8 @@ export let serve = async (
122165
}
123166
res.sendStatus(200);
124167
})
125-
.listen(options.httpPort, () => {
126-
console.log(`dev server listening on port ${options.httpPort}`);
168+
.listen(options.httpOrigin.port, () => {
169+
console.log("Remix dev server ready");
127170
});
128171

129172
return new Promise(() => {}).finally(async () => {

packages/remix-server-runtime/dev.ts

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,17 @@
11
import type { ServerBuild } from "./build";
22

3-
export let devReady = (
4-
build: ServerBuild,
5-
options: {
6-
scheme?: string;
7-
host?: string;
8-
port?: number;
9-
} = {}
10-
) => {
11-
let scheme = options.scheme ?? "http";
12-
let host = options.host ?? "localhost";
13-
let port = options.port ?? Number(process.env.REMIX_DEV_HTTP_PORT);
14-
if (!port) throw Error("Dev server port not set");
15-
if (isNaN(port))
16-
throw Error(
17-
`Dev server port must be a number. Got: ${JSON.stringify(port)}`
18-
);
3+
export let devReady = (build: ServerBuild, origin?: string) => {
4+
origin ??= process.env.REMIX_DEV_HTTP_ORIGIN;
5+
if (!origin) throw Error("Dev server origin not set");
196

20-
fetch(`${scheme}://${host}:${port}/ping`, {
21-
method: "POST",
22-
headers: { "Content-Type": "application/json" },
23-
body: JSON.stringify({ buildHash: build.assets.version }),
24-
});
7+
try {
8+
fetch(`${origin}/ping`, {
9+
method: "POST",
10+
headers: { "Content-Type": "application/json" },
11+
body: JSON.stringify({ buildHash: build.assets.version }),
12+
});
13+
} catch (error) {
14+
console.error(`Could not reach Remix dev server at ${origin}`);
15+
throw error;
16+
}
2517
};

0 commit comments

Comments
 (0)