Skip to content

Commit 8f4a161

Browse files
authored
fix(dev): handle new routes gracefully (#6165)
1 parent 056f6b5 commit 8f4a161

File tree

4 files changed

+58
-64
lines changed

4 files changed

+58
-64
lines changed

packages/remix-dev/cli/commands.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ export async function build(
187187
process.exit(1);
188188
});
189189

190-
console.log(`built in ${prettyMs(Date.now() - start)}`);
190+
console.log(`Built in ${prettyMs(Date.now() - start)}`);
191191
}
192192

193193
// TODO: replace watch in v2
@@ -203,10 +203,7 @@ export async function watch(
203203
? remixRootOrConfig
204204
: await readConfig(remixRootOrConfig);
205205

206-
devServer.liveReload(config, {
207-
onInitialBuild: (durationMs) =>
208-
console.log(`💿 Built in ${prettyMs(durationMs)}`),
209-
});
206+
devServer.liveReload(config);
210207
return await new Promise(() => {});
211208
}
212209

packages/remix-dev/compiler/watch.ts

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,68 +21,67 @@ function isEntryPoint(config: RemixConfig, file: string): boolean {
2121

2222
export type WatchOptions = {
2323
reloadConfig?(root: string): Promise<RemixConfig>;
24-
onRebuildStart?(): void;
25-
onRebuildFinish?(durationMs: number, manifest?: Manifest): void;
24+
onBuildStart?(ctx: Context): void;
25+
onBuildFinish?(ctx: Context, durationMs: number, manifest?: Manifest): void;
2626
onFileCreated?(file: string): void;
2727
onFileChanged?(file: string): void;
2828
onFileDeleted?(file: string): void;
29-
onInitialBuild?(durationMs: number, manifest?: Manifest): void;
3029
};
3130

3231
export async function watch(
33-
{ config, options }: Context,
32+
ctx: Context,
3433
{
3534
reloadConfig = readConfig,
36-
onRebuildStart,
37-
onRebuildFinish,
35+
onBuildStart,
36+
onBuildFinish,
3837
onFileCreated,
3938
onFileChanged,
4039
onFileDeleted,
41-
onInitialBuild,
4240
}: WatchOptions = {}
4341
): Promise<() => Promise<void>> {
4442
let start = Date.now();
45-
let compiler = await Compiler.create({ config, options });
43+
let compiler = await Compiler.create(ctx);
4644
let compile = () =>
4745
compiler.compile().catch((thrown) => {
4846
logThrown(thrown);
4947
return undefined;
5048
});
5149

5250
// initial build
51+
onBuildStart?.(ctx);
5352
let manifest = await compile();
54-
onInitialBuild?.(Date.now() - start, manifest);
53+
onBuildFinish?.(ctx, Date.now() - start, manifest);
5554

5655
let restart = debounce(async () => {
57-
onRebuildStart?.();
56+
onBuildStart?.(ctx);
5857
let start = Date.now();
5958
compiler.dispose();
6059

6160
try {
62-
config = await reloadConfig(config.rootDirectory);
61+
ctx.config = await reloadConfig(ctx.config.rootDirectory);
6362
} catch (thrown: unknown) {
6463
logThrown(thrown);
6564
return;
6665
}
6766

68-
compiler = await Compiler.create({ config, options });
67+
compiler = await Compiler.create(ctx);
6968
let manifest = await compile();
70-
onRebuildFinish?.(Date.now() - start, manifest);
69+
onBuildFinish?.(ctx, Date.now() - start, manifest);
7170
}, 500);
7271

7372
let rebuild = debounce(async () => {
74-
onRebuildStart?.();
73+
onBuildStart?.(ctx);
7574
let start = Date.now();
7675
let manifest = await compile();
77-
onRebuildFinish?.(Date.now() - start, manifest);
76+
onBuildFinish?.(ctx, Date.now() - start, manifest);
7877
}, 100);
7978

80-
let toWatch = [config.appDirectory];
81-
if (config.serverEntryPoint) {
82-
toWatch.push(config.serverEntryPoint);
79+
let toWatch = [ctx.config.appDirectory];
80+
if (ctx.config.serverEntryPoint) {
81+
toWatch.push(ctx.config.serverEntryPoint);
8382
}
8483

85-
config.watchPaths?.forEach((watchPath) => {
84+
ctx.config.watchPaths?.forEach((watchPath) => {
8685
toWatch.push(watchPath);
8786
});
8887

@@ -104,17 +103,17 @@ export async function watch(
104103
onFileCreated?.(file);
105104

106105
try {
107-
config = await reloadConfig(config.rootDirectory);
106+
ctx.config = await reloadConfig(ctx.config.rootDirectory);
108107
} catch (thrown: unknown) {
109108
logThrown(thrown);
110109
return;
111110
}
112111

113-
await (isEntryPoint(config, file) ? restart : rebuild)();
112+
await (isEntryPoint(ctx.config, file) ? restart : rebuild)();
114113
})
115114
.on("unlink", async (file) => {
116115
onFileDeleted?.(file);
117-
await (isEntryPoint(config, file) ? restart : rebuild)();
116+
await (isEntryPoint(ctx.config, file) ? restart : rebuild)();
118117
});
119118

120119
return async () => {

packages/remix-dev/devServer/liveReload.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import path from "path";
44
import prettyMs from "pretty-ms";
55
import WebSocket from "ws";
66

7-
import type { WatchOptions } from "../compiler";
87
import { watch } from "../compiler";
98
import type { RemixConfig } from "../config";
109
import { warnOnce } from "../warnOnce";
@@ -19,10 +18,7 @@ let clean = (config: RemixConfig) => {
1918
}
2019
};
2120

22-
export async function liveReload(
23-
config: RemixConfig,
24-
{ onInitialBuild }: WatchOptions = {}
25-
) {
21+
export async function liveReload(config: RemixConfig) {
2622
clean(config);
2723
let wss = new WebSocket.Server({ port: config.devServerPort });
2824
function broadcast(event: { type: string } & Record<string, unknown>) {
@@ -41,6 +37,7 @@ export async function liveReload(
4137
broadcast({ type: "LOG", message: _message });
4238
}
4339

40+
let hasBuilt = false;
4441
let dispose = await watch(
4542
{
4643
config,
@@ -51,13 +48,14 @@ export async function liveReload(
5148
},
5249
},
5350
{
54-
onInitialBuild,
55-
onRebuildStart() {
51+
onBuildStart() {
5652
clean(config);
57-
log("Rebuilding...");
53+
log((hasBuilt ? "Rebuilding" : "Building") + "...");
5854
},
59-
onRebuildFinish(durationMs: number) {
60-
log(`Rebuilt in ${prettyMs(durationMs)}`);
55+
onBuildFinish(_, durationMs: number, manifest) {
56+
if (manifest === undefined) return;
57+
hasBuilt = true;
58+
log((hasBuilt ? "Rebuilt" : "Built") + ` in ${prettyMs(durationMs)}`);
6159
broadcast({ type: "RELOAD" });
6260
},
6361
onFileCreated(file) {

packages/remix-dev/devServer_unstable/index.ts

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ import { warnOnce } from "../warnOnce";
1515
import { detectPackageManager } from "../cli/detectPackageManager";
1616

1717
export let serve = async (
18-
config: RemixConfig,
18+
initialConfig: RemixConfig,
1919
options: {
2020
command?: string;
2121
httpPort: number;
2222
websocketPort: number;
2323
restart: boolean;
2424
}
2525
) => {
26-
await loadEnv(config.rootDirectory);
26+
await loadEnv(initialConfig.rootDirectory);
2727
let websocket = Socket.serve({ port: options.websocketPort });
2828

2929
let state: {
@@ -36,6 +36,7 @@ export let serve = async (
3636
let pkgManager = detectPackageManager() ?? "npm";
3737
let bin = (await execa(pkgManager, ["bin"])).stdout.trim();
3838
let startAppServer = (command: string) => {
39+
console.log(`> ${command}`);
3940
return execa.command(command, {
4041
stdio: "inherit",
4142
env: {
@@ -48,7 +49,7 @@ export let serve = async (
4849

4950
let dispose = await Compiler.watch(
5051
{
51-
config,
52+
config: initialConfig,
5253
options: {
5354
mode: "development",
5455
sourcemap: true,
@@ -58,45 +59,44 @@ export let serve = async (
5859
},
5960
},
6061
{
61-
onInitialBuild: (durationMs, manifest) => {
62-
console.info(`💿 Built in ${prettyMs(durationMs)}`);
63-
state.prevManifest = manifest;
64-
if (options.command && manifest) {
65-
console.log(`starting: ${options.command}`);
66-
state.appServer = startAppServer(options.command);
67-
}
68-
},
69-
onRebuildStart: () => {
62+
onBuildStart: (ctx) => {
7063
state.buildHashChannel?.err();
71-
clean(config);
72-
websocket.log("Rebuilding...");
64+
clean(ctx.config);
65+
websocket.log(state.prevManifest ? "Rebuilding..." : "Building...");
7366
},
74-
onRebuildFinish: async (durationMs, manifest) => {
67+
onBuildFinish: async (ctx, durationMs, manifest) => {
7568
if (!manifest) return;
76-
websocket.log(`Rebuilt in ${prettyMs(durationMs)}`);
7769

78-
// TODO: should we restart the app server when build failed?
70+
websocket.log(
71+
(state.prevManifest ? "Rebuilt" : "Built") +
72+
` in ${prettyMs(durationMs)}`
73+
);
7974
state.latestBuildHash = manifest.version;
8075
state.buildHashChannel = Channel.create();
81-
console.log(`Waiting (${state.latestBuildHash})`);
82-
if (state.appServer === undefined || options.restart) {
83-
console.log(`restarting: ${options.command}`);
76+
77+
let start = Date.now();
78+
console.log(`Waiting for app server (${state.latestBuildHash})`);
79+
if (
80+
options.command &&
81+
(state.appServer === undefined || options.restart)
82+
) {
8483
await kill(state.appServer);
85-
if (options.command) {
86-
state.appServer = startAppServer(options.command);
87-
}
84+
state.appServer = startAppServer(options.command);
8885
}
8986
let { ok } = await state.buildHashChannel.result;
9087
// result not ok -> new build started before this one finished. do not process outdated manifest
9188
if (!ok) return;
89+
console.log(`App server took ${prettyMs(Date.now() - start)}`);
9290

9391
if (manifest.hmr && state.prevManifest) {
94-
let updates = HMR.updates(config, manifest, state.prevManifest);
92+
let updates = HMR.updates(ctx.config, manifest, state.prevManifest);
9593
websocket.hmr(manifest, updates);
96-
console.log("> HMR");
97-
} else {
94+
95+
let hdr = updates.some((u) => u.revalidate);
96+
console.log("> HMR" + (hdr ? " + HDR" : ""));
97+
} else if (state.prevManifest !== undefined) {
9898
websocket.reload();
99-
console.log("> Reload");
99+
console.log("> Live reload");
100100
}
101101
state.prevManifest = manifest;
102102
},

0 commit comments

Comments
 (0)