Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions client-src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ export function setOptionsAndConnect(overrides) {
// eslint-disable-next-line jsdoc/reject-any-type
/** @typedef {any} EXPECTED_ANY */

/** @typedef {{ name?: string, errors: string[], warnings: string[], hash: string, time?: number, modules?: Record<string, string>, action?: string }} HMRPayload */
/** @typedef {{ name?: string, errors: string[], warnings: string[], hash: string, time?: number, modules?: Record<string, string>, action?: string, file?: string }} HMRPayload */

/**
* @returns {{
Expand Down Expand Up @@ -269,7 +269,11 @@ let subscribeAllHandler;
function processMessage(obj) {
switch (obj.action) {
case "building": {
log.info(`bundle ${obj.name ? `'${obj.name}' ` : ""}rebuilding`);
log.info(
`bundle ${obj.name ? `'${obj.name}' ` : ""}rebuilding${
obj.file ? ` (${obj.file} changed)` : ""
}`,
);
break;
}
case "built":
Expand Down
15 changes: 13 additions & 2 deletions src/hot.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
/**
* @typedef {object} Payload
* @property {string} action action
* @property {string=} file file that invalidated the compilation
* @property {string=} name name
* @property {number=} time time
* @property {string=} hash hash
Expand Down Expand Up @@ -282,12 +283,22 @@ function createHot(compiler, userOptions) {
let latestStats = null;
let closed = false;

const onInvalid = () => {
/** @param {string | null=} fileName file that triggered the rebuild */
const onInvalid = (fileName) => {
if (closed) return;

latestStats = null;

eventStream.publish({ action: "building" });
/** @type {{ action: string, file?: string }} */
const payload = { action: "building" };

// The invalid hook reports which file changed — forward it so clients
// can show what triggered the rebuild.
if (typeof fileName === "string" && fileName) {
payload.file = fileName;
}

eventStream.publish(payload);
};

/** @param {Stats | MultiStats} statsResult stats result */
Expand Down
11 changes: 11 additions & 0 deletions test/client.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,17 @@ describe("client", () => {
expect(processUpdate).toHaveBeenCalledTimes(1);
});

it("logs the changed file on building messages", () => {
EventSourceStub.lastInstance().onmessage(
makeMessage({ action: "building", file: "/src/index.js" }),
);
expect(
console.info.mock.calls.some(([msg]) =>
msg.includes("rebuilding (/src/index.js changed)"),
),
).toBe(true);
});

it("calls subscribeAll handler on default messages", () => {
const spy = jest.fn();
client.subscribeAll(spy);
Expand Down
1 change: 1 addition & 0 deletions test/helpers/sse.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import http from "node:http";
/**
* @typedef {object} SseEvent
* @property {string=} action event action (building/built/sync/custom)
* @property {string=} file file that invalidated the compilation
* @property {string=} name compilation name
* @property {string=} hash compilation hash
* @property {number=} time build time in ms
Expand Down
36 changes: 34 additions & 2 deletions test/hot.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ function makeFakeCompiler(logger = noopLogger) {
done: { tap: (_name, fn) => doneTaps.push(fn) },
},
getInfrastructureLogger: () => logger,
emitInvalid() {
for (const fn of invalidTaps) fn();
emitInvalid(fileName) {
for (const fn of invalidTaps) fn(fileName);
},
emitDone(stats) {
for (const fn of doneTaps) fn(stats);
Expand Down Expand Up @@ -327,6 +327,38 @@ describe("createHot", () => {
hot.close();
});

it("includes the changed file in the building payload", () => {
const compiler = makeFakeCompiler();
const hot = createHot(compiler, {});
const { writes } = attachClient({ handler: hot.handle });

compiler.emitInvalid("/src/index.js");

expect(
writes.some(
(w) =>
w.includes('"action":"building"') &&
w.includes('"file":"/src/index.js"'),
),
).toBe(true);

hot.close();
});

it("omits the file field when the invalid hook reports none", () => {
const compiler = makeFakeCompiler();
const hot = createHot(compiler, {});
const { writes } = attachClient({ handler: hot.handle });

compiler.emitInvalid();

const building = writes.find((w) => w.includes('"action":"building"'));
expect(building).toBeDefined();
expect(building).not.toContain('"file"');

hot.close();
});

it("sends a sync payload to a client that connects after a build", () => {
const compiler = makeFakeCompiler();
const hot = createHot(compiler, {});
Expand Down
5 changes: 5 additions & 0 deletions types/hot.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ declare const HOT_DEFAULT_HEARTBEAT: number;
/**
* @typedef {object} Payload
* @property {string} action action
* @property {string=} file file that invalidated the compilation
* @property {string=} name name
* @property {number=} time time
* @property {string=} hash hash
Expand Down Expand Up @@ -168,6 +169,10 @@ type Payload = {
* action
*/
action: string;
/**
* file that invalidated the compilation
*/
file?: string | undefined;
/**
* name
*/
Expand Down
Loading