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
13 changes: 8 additions & 5 deletions packages/logger/lib/loggers/Serve.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,15 @@ class Serve extends Logger {
this.#emitStatus("serve-ready", {}, `Server ready`);
}

stale(changedProjects) {
if (!changedProjects || !Array.isArray(changedProjects)) {
throw new Error("loggers/Serve#stale: Missing or incorrect changedProjects parameter");
// Emits the current stale-project set. An empty list means every project is up-to-date.
stale(staleProjects) {
if (!staleProjects || !Array.isArray(staleProjects)) {
throw new Error("loggers/Serve#stale: Missing or incorrect staleProjects parameter");
}
this.#emitStatus("serve-stale", {changedProjects},
`Sources changed in: ${changedProjects.join(", ")}`);
this.#emitStatus("serve-stale", {staleProjects},
staleProjects.length ?
`Stale projects: ${staleProjects.join(", ")}` :
`All projects up-to-date`);
}

settling(pendingProjects) {
Expand Down
8 changes: 5 additions & 3 deletions packages/logger/lib/writers/InteractiveConsole.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {createHeaderState, setTool} from "./interactiveConsole/state/header.js";
import {createProjectState, setProject, enableProjectPlaceholders} from "./interactiveConsole/state/project.js";
import {createServerState, setListening, enableServerPlaceholders} from "./interactiveConsole/state/server.js";
import {
createBuildState, beginBuild, advanceToProject, setTask, transitionTo, setError, STATES,
createBuildState, beginBuild, advanceToProject, setTask, transitionTo, setError, setStale, STATES,
SPINNING_STATES, enableBuildPlaceholders,
} from "./interactiveConsole/state/build.js";
import {
Expand Down Expand Up @@ -346,8 +346,10 @@ class InteractiveConsole {
this.#transitionTo(STATES.READY);
break;
case "serve-stale":
this.#buildState.changedProjects = evt.changedProjects || [];
this.#transitionTo(STATES.STALE);
// Record the stale set and re-render without a state transition, so the count updates
// in place beneath the current status (typically `ready`).
setStale(this.#buildState, evt.staleProjects);
this.#render();
break;
case "serve-settling":
this.#buildState.pendingProjects = evt.pendingProjects || [];
Expand Down
13 changes: 9 additions & 4 deletions packages/logger/lib/writers/interactiveConsole/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {REMOTE_CONNECTIONS_WARNING_LINES} from "./remoteConnectionsWarning.js";
const SPINNER_FRAMES = ["◐", "◓", "◑", "◒"];
const STATE_LABEL_WIDTH = "building".length;
const pad = (s) => s.padEnd(STATE_LABEL_WIDTH);
// "1 project" / "2 projects" - shared by the stale (ready) and validating counts.
const projectCount = (n) => `${n} project${n === 1 ? "" : "s"}`;

// Align continuation lines (additional network URLs / extra placeholders)
// under the first URL slot. The arrow, single space, and "Network: " label
Expand Down Expand Up @@ -145,11 +147,14 @@ function renderStatusLine(state) {
if (state.lastBuildHrtime) {
suffix = ` ${chalk.dim("·")} ${chalk.dim("Time elapsed: " + prettyHrtime(state.lastBuildHrtime))}`;
}
// Append the stale count to the ready line.
const staleCount = state.staleProjects.length;
if (staleCount > 0) {
suffix += ` ${chalk.dim("·")} ` +
chalk.yellow(`${projectCount(staleCount)} stale`);
}
return `${label}${chalk.green(figures.bullet)} ${chalk.green(pad("ready"))}${suffix}`;
}
case STATES.STALE:
return `${label}${chalk.yellow(figures.circle)} ${chalk.yellow(pad("stale"))} ` +
`${chalk.dim("· files changed, rebuild on next request")}`;
case STATES.SETTLING: {
const frame = SPINNER_FRAMES[state.spinFrame % SPINNER_FRAMES.length];
return `${label}${chalk.yellow(frame)} ${chalk.yellow(pad("settling"))} ` +
Expand All @@ -164,7 +169,7 @@ function renderStatusLine(state) {
];
const count = state.validatingProjects.length;
if (count > 0) {
parts.push(chalk.dim("·"), chalk.dim(`${count} project${count === 1 ? "" : "s"}`));
parts.push(chalk.dim("·"), chalk.dim(projectCount(count)));
}
return `${label}${parts.join(" ")}`;
}
Expand Down
13 changes: 9 additions & 4 deletions packages/logger/lib/writers/interactiveConsole/state/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ export const STATES = Object.freeze({
INITIAL: "initial",
STARTING: "starting", // pre-populated placeholder before the first real state arrives
READY: "ready",
STALE: "stale",
SETTLING: "settling", // changes seen, rebuild deferred until they quiesce
BUILDING: "building",
VALIDATING: "validating",
Expand All @@ -26,9 +25,9 @@ export function createBuildState() {
totalProjects: 0,
currentProjectName: "",
currentTaskName: "",
// Names of projects collected via `serve-stale` payloads — used to label
// the stale state if/when the renderer wants to.
changedProjects: [],
// Names of projects currently stale, reported via `serve-stale`. The renderer surfaces
// the count alongside the ready line.
staleProjects: [],
// Names of projects collected via `serve-validating` payloads — used to
// label the validating state if/when the renderer wants to.
validatingProjects: [],
Expand Down Expand Up @@ -92,6 +91,12 @@ export function transitionTo(state, newState) {
state.spinFrame = 0;
}

// Record the current stale-project set, reported via `serve-stale`. Does not touch the
// activity `state` or reset the spinner: the count updates in place beneath the current status.
export function setStale(state, staleProjects) {
state.staleProjects = Array.isArray(staleProjects) ? staleProjects : [];
}

export function setError(state, message) {
state.errorMessage = message || "";
transitionTo(state, STATES.ERROR);
Expand Down
27 changes: 24 additions & 3 deletions packages/logger/test/lib/loggers/Serve.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,18 @@ test.serial("stale emits serve-stale", (t) => {
t.deepEqual(statusHandler.getCall(0).args[0], {
level: "info",
status: "serve-stale",
changedProjects: ["project.a", "project.b"],
staleProjects: ["project.a", "project.b"],
}, "Status event has expected payload");
});

test.serial("stale emits serve-stale with an empty set", (t) => {
const {serveLogger, statusHandler} = t.context;
serveLogger.stale([]);
t.is(statusHandler.callCount, 1, "One serve-status event emitted");
t.deepEqual(statusHandler.getCall(0).args[0], {
level: "info",
status: "serve-stale",
staleProjects: [],
}, "Status event has expected payload");
});

Expand All @@ -52,7 +63,7 @@ test.serial("stale: Missing parameter", (t) => {
t.throws(() => {
serveLogger.stale();
}, {
message: "loggers/Serve#stale: Missing or incorrect changedProjects parameter",
message: "loggers/Serve#stale: Missing or incorrect staleProjects parameter",
}, "Threw with expected error message");
});

Expand Down Expand Up @@ -164,11 +175,21 @@ test.serial("No event listener: stale", (t) => {
t.is(logStub.callCount, 1, "_log got called once");
t.is(logStub.getCall(0).args[0], "info", "Logged with expected log-level");
t.is(logStub.getCall(0).args[1],
"Sources changed in: project.a, project.b",
"Stale projects: project.a, project.b",
"Logged expected message");
t.is(logHandler.callCount, 0, "No log event emitted");
});

test.serial("No event listener: stale with an empty set", (t) => {
const {serveLogger, statusHandler, logHandler, logStub} = t.context;
process.off(ServeLogger.SERVE_STATUS_EVENT_NAME, statusHandler);
serveLogger.stale([]);
t.is(logStub.callCount, 1, "_log got called once");
t.is(logStub.getCall(0).args[0], "info", "Logged with expected log-level");
t.is(logStub.getCall(0).args[1], "All projects up-to-date", "Logged expected message");
t.is(logHandler.callCount, 0, "No log event emitted");
});

test.serial("No event listener: validating", (t) => {
const {serveLogger, statusHandler, logHandler, logStub} = t.context;
process.off(ServeLogger.SERVE_STATUS_EVENT_NAME, statusHandler);
Expand Down
11 changes: 6 additions & 5 deletions packages/logger/test/lib/writers/InteractiveConsole.js
Original file line number Diff line number Diff line change
Expand Up @@ -554,19 +554,20 @@ test.serial("serve-status: serve-build-done without a valid hrtime records null"
writer.disable();
});

test.serial("serve-status: serve-stale records changedProjects and transitions to STALE", (t) => {
test.serial("serve-status: serve-stale records staleProjects without a state transition", (t) => {
const {writer} = createWriter();
process.emit("ui5.serve-status", {status: "serve-stale", changedProjects: ["my.app"]});
process.emit("ui5.serve-status", {status: "serve-ready"});
process.emit("ui5.serve-status", {status: "serve-stale", staleProjects: ["my.app"]});
const state = writer._getStateForTest().build;
t.is(state.state, STATES.STALE);
t.deepEqual(state.changedProjects, ["my.app"]);
t.is(state.state, STATES.READY, "the stale report does not change the activity state");
t.deepEqual(state.staleProjects, ["my.app"]);
writer.disable();
});

test.serial("serve-status: serve-stale without a payload falls back to an empty list", (t) => {
const {writer} = createWriter();
process.emit("ui5.serve-status", {status: "serve-stale"});
t.deepEqual(writer._getStateForTest().build.changedProjects, []);
t.deepEqual(writer._getStateForTest().build.staleProjects, []);
writer.disable();
});

Expand Down
24 changes: 19 additions & 5 deletions packages/logger/test/lib/writers/interactiveConsole/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,11 +246,27 @@ test("renderBuildRegion: ready state with hrtime shows elapsed suffix", (t) => {
t.regex(plain, /ready.*Time elapsed:/);
});

test("renderBuildRegion: stale state includes 'files changed' hint", (t) => {
test("renderBuildRegion: ready state appends a singular stale count", (t) => {
const state = createBuildState();
transitionTo(state, STATES.STALE);
state.staleProjects = ["library.a"];
transitionTo(state, STATES.READY);
const plain = renderBuildRegion(state).map(stripAnsi).join("\n");
t.regex(plain, /ready.*·\s+1 project(?!s) stale/);
});

test("renderBuildRegion: ready state appends a plural stale count", (t) => {
const state = createBuildState();
state.staleProjects = ["library.a", "library.b"];
transitionTo(state, STATES.READY);
const plain = renderBuildRegion(state).map(stripAnsi).join("\n");
t.regex(plain, /ready.*·\s+2 projects stale/);
});

test("renderBuildRegion: ready state omits the stale count when everything is fresh", (t) => {
const state = createBuildState();
transitionTo(state, STATES.READY);
const plain = renderBuildRegion(state).map(stripAnsi).join("\n");
t.regex(plain, /Status\s+.+?\s+stale\s+·\s+files changed/);
t.notRegex(plain, /stale/, "no stale fragment when staleProjects is empty");
});

test("renderBuildRegion: settling state includes 'waiting for changes to settle' hint", (t) => {
Expand Down Expand Up @@ -382,7 +398,6 @@ test("renderBuildRegion: building state renders bare project/task names when no
const GLYPH_BY_STATE = [
{state: STATES.STARTING, glyph: figures.circle, name: "circle"},
{state: STATES.READY, glyph: figures.bullet, name: "bullet"},
{state: STATES.STALE, glyph: figures.circle, name: "circle"},
{state: STATES.ERROR, glyph: figures.cross, name: "cross"},
];
for (const {state: s, glyph, name} of GLYPH_BY_STATE) {
Expand All @@ -404,7 +419,6 @@ const WIDTH = "building".length;
const COLOR_BY_STATE = [
{state: STATES.STARTING, wrap: (x) => chalk.dim(x), word: "starting", name: "dim"},
{state: STATES.READY, wrap: (x) => chalk.green(x), word: "ready", name: "green"},
{state: STATES.STALE, wrap: (x) => chalk.yellow(x), word: "stale", name: "yellow"},
{state: STATES.BUILDING, wrap: (x) => chalk.yellow(x), word: "building", name: "yellow"},
{state: STATES.VALIDATING, wrap: (x) => chalk.cyan(x), word: "validating cache", name: "cyan"},
{state: STATES.ERROR, wrap: (x) => chalk.red(x), word: "error", name: "red"},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
setTask,
transitionTo,
setError,
setStale,
enableBuildPlaceholders,
STATES,
} from "../../../../../lib/writers/interactiveConsole/state/build.js";
Expand All @@ -17,7 +18,7 @@ test("createBuildState: starts in INITIAL with empty counters", (t) => {
t.is(state.currentProjectIndex, 0);
t.is(state.totalProjects, 0);
t.deepEqual(state.projectOrder, []);
t.deepEqual(state.changedProjects, []);
t.deepEqual(state.staleProjects, []);
t.deepEqual(state.validatingProjects, []);
t.is(state.spinFrame, 0);
t.is(state.errorMessage, "");
Expand Down Expand Up @@ -106,6 +107,23 @@ test("setError: falsy message resolves to an empty string", (t) => {
t.is(state.errorMessage, "");
});

test("setStale: records the stale set without touching the activity state", (t) => {
const state = createBuildState();
transitionTo(state, STATES.READY);
state.spinFrame = 3;
setStale(state, ["library.a", "library.b"]);
t.deepEqual(state.staleProjects, ["library.a", "library.b"]);
t.is(state.state, STATES.READY, "activity state is unchanged");
t.is(state.spinFrame, 3, "spinner frame is not reset");
});

test("setStale: a non-array resolves to an empty set", (t) => {
const state = createBuildState();
state.staleProjects = ["stale"];
setStale(state, undefined);
t.deepEqual(state.staleProjects, []);
});

test("enableBuildPlaceholders: promotes INITIAL to STARTING", (t) => {
const state = createBuildState();
enableBuildPlaceholders(state);
Expand Down
Loading
Loading