Skip to content

Commit ac95e46

Browse files
committed
refactor: Redundant cleanup summary
1 parent f6e0404 commit ac95e46

4 files changed

Lines changed: 100 additions & 220 deletions

File tree

packages/cli/lib/cli/commands/cache.js

Lines changed: 20 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import baseMiddleware from "../middlewares/base.js";
66
import {getUi5DataDir} from "../../framework/utils.js";
77
import * as frameworkCache from "@ui5/project/ui5Framework/cache";
88
import CacheManager from "@ui5/project/build/cache/CacheManager";
9-
import prettyHrtime from "pretty-hrtime";
109

1110
const cacheCommand = {
1211
command: "cache",
@@ -33,7 +32,17 @@ cacheCommand.builder = function(cli) {
3332
.example("$0 cache clean --yes",
3433
"Remove all cached UI5 data without confirmation (e.g. in CI)")
3534
.example("UI5_DATA_DIR=/custom/path $0 cache clean",
36-
"Remove cached data from a non-default UI5 data directory");
35+
"Remove cached data from a non-default UI5 data directory")
36+
.epilogue(
37+
"The cache is stored in the UI5 data directory (default: ~/.ui5).\n" +
38+
"Override the location with the UI5_DATA_DIR environment variable or\n" +
39+
"the 'ui5DataDir' configuration option (see 'ui5 config --help').\n\n" +
40+
"Two cache types are removed:\n" +
41+
" UI5 Framework packages Downloaded UI5 library files " +
42+
"(~/.ui5/framework/)\n" +
43+
" Build cache (DB) Incremental build data " +
44+
"(~/.ui5/buildCache/)"
45+
);
3746
},
3847
middlewares: [baseMiddleware],
3948
});
@@ -62,19 +71,17 @@ function formatSize(bytes) {
6271
}
6372

6473
/**
65-
* Format a library stats detail string, e.g. "2 projects, 3 libraries, 4 versions".
66-
* Each word is independently singular/plural.
74+
* Format framework cache stats as a human-readable detail string.
75+
* E.g. "1,189 versions of 155 libraries" or "1 version of 1 library".
6776
*
6877
* @param {number} libraryCount
69-
* @param {number} projectCount
7078
* @param {number} versionCount
7179
* @returns {string}
7280
*/
73-
function formatLibraryStats(libraryCount, projectCount, versionCount) {
74-
const p = `${projectCount} ${projectCount === 1 ? "project" : "projects"}`;
75-
const l = `${libraryCount} ${libraryCount === 1 ? "library" : "libraries"}`;
76-
const v = `${versionCount} ${versionCount === 1 ? "version" : "versions"}`;
77-
return `${p}, ${l}, ${v}`;
81+
function formatFrameworkStats(libraryCount, versionCount) {
82+
const v = `${versionCount.toLocaleString("en-US")} ${versionCount === 1 ? "version" : "versions"}`;
83+
const l = `${libraryCount.toLocaleString("en-US")} ${libraryCount === 1 ? "library" : "libraries"}`;
84+
return `${v} of ${l}`;
7885
}
7986

8087
/**
@@ -87,64 +94,6 @@ function padLabel(label) {
8794
return label.padEnd(LABEL_WIDTH);
8895
}
8996

90-
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
91-
const PROGRESS_DEBOUNCE_MS = 150;
92-
// Reserve enough columns for the fixed parts of the progress line so the path
93-
// never causes the line to wrap on a standard 80-column terminal.
94-
const PATH_MAX_COLS = 40;
95-
96-
/**
97-
* Build a progress handler for framework cache deletion.
98-
* Returns a function to pass as onProgress to cleanCache(), plus a finalise()
99-
* to call when deletion completes (clears the in-progress line).
100-
*
101-
* The line is written to stderr with \r so it overwrites itself on each tick,
102-
* producing a single updating line rather than a scrolling log.
103-
*
104-
* @param {string} label Short label shown on the progress line
105-
* @param {Array} startHrtime process.hrtime() snapshot taken when deletion began
106-
* @param {Function} prettyHrtime Formatting function from the pretty-hrtime package
107-
* @returns {{onProgress: function(string): void, finalise: function(): void}}
108-
*/
109-
function createProgressHandler(label, startHrtime, prettyHrtime) {
110-
let lastPrintMs = 0;
111-
let frameIndex = 0;
112-
let lastVisibleLen = 0;
113-
114-
function onProgress(entryPath) {
115-
const now = Date.now();
116-
if (now - lastPrintMs < PROGRESS_DEBOUNCE_MS) return;
117-
lastPrintMs = now;
118-
119-
const elapsed = prettyHrtime(process.hrtime(startHrtime));
120-
const spinner = SPINNER_FRAMES[frameIndex % SPINNER_FRAMES.length];
121-
frameIndex++;
122-
123-
// Trim path so the whole line stays within 80 columns
124-
let displayPath = entryPath;
125-
if (displayPath.length > PATH_MAX_COLS) {
126-
displayPath = "…" + displayPath.slice(-(PATH_MAX_COLS - 1));
127-
}
128-
129-
// Build visible text (no ANSI) first to get accurate length for overwrite padding
130-
const visibleText = ` ${spinner} ${label} ${displayPath} ${elapsed}`;
131-
// Then the styled version for actual output
132-
const styledText = ` ${spinner} ${label} ${chalk.dim(displayPath)} ${elapsed}`;
133-
134-
// Pad to cover any longer previous line, then overwrite in place
135-
const padded = styledText + " ".repeat(Math.max(0, lastVisibleLen - visibleText.length));
136-
lastVisibleLen = visibleText.length;
137-
138-
process.stderr.write(`\r${padded}`);
139-
}
140-
141-
function finalise() {
142-
process.stderr.write(`\r${" ".repeat(lastVisibleLen)}\r`);
143-
}
144-
145-
return {onProgress, finalise};
146-
}
147-
14897
async function handleCache(argv) {
14998
// Resolve UI5 data directory — uses the same resolution chain as ui5 build/serve:
15099
// UI5_DATA_DIR env var → ui5DataDir config (~/.ui5rc) → default ~/.ui5
@@ -185,8 +134,7 @@ async function handleCache(argv) {
185134
// Display items that will be removed
186135
process.stderr.write(chalk.bold("\nThe following cached data will be removed:\n\n"));
187136
if (frameworkInfo) {
188-
const detail = formatLibraryStats(
189-
frameworkInfo.libraryCount, frameworkInfo.projectCount, frameworkInfo.versionCount);
137+
const detail = formatFrameworkStats(frameworkInfo.libraryCount, frameworkInfo.versionCount);
190138
process.stderr.write(
191139
` ${chalk.yellow("•")} ${padLabel(LABEL_FRAMEWORK)} ${frameworkAbsPath} (${detail})\n`
192140
);
@@ -213,22 +161,12 @@ async function handleCache(argv) {
213161
}
214162

215163
// Perform the actual cleanup (orchestrate both domains)
216-
let frameworkResult;
217-
if (frameworkInfo) {
218-
const startHrtime = process.hrtime();
219-
const {onProgress, finalise} = createProgressHandler(LABEL_FRAMEWORK, startHrtime, prettyHrtime);
220-
try {
221-
frameworkResult = await frameworkCache.cleanCache(ui5DataDir, onProgress);
222-
} finally {
223-
finalise();
224-
}
225-
}
164+
const frameworkResult = await frameworkCache.cleanCache(ui5DataDir);
226165
const buildResult = await CacheManager.cleanCache(ui5DataDir);
227166

228167
process.stderr.write("\n");
229168
if (frameworkResult) {
230-
const detail = formatLibraryStats(
231-
frameworkResult.libraryCount, frameworkResult.projectCount, frameworkResult.versionCount);
169+
const detail = formatFrameworkStats(frameworkResult.libraryCount, frameworkResult.versionCount);
232170
process.stderr.write(
233171
`${chalk.green("✓")} Removed ${chalk.bold(LABEL_FRAMEWORK)}` +
234172
` (${frameworkAbsPath} · ${detail})\n`

packages/cli/test/lib/cli/commands/cache.js

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ function getDefaultArgv() {
1919
// Stable absolute path used as the resolved ui5DataDir in most tests
2020
const TEST_UI5_DATA_DIR = path.resolve("/test/ui5/home");
2121

22-
// Typical framework stub result shape
23-
const FRAMEWORK_STUB = {path: "framework", projectCount: 2, libraryCount: 18, versionCount: 5};
22+
// Typical framework stub result shape: { path, libraryCount, versionCount }
23+
const FRAMEWORK_STUB = {path: "framework", libraryCount: 18, versionCount: 5};
2424

2525
test.beforeEach(async (t) => {
2626
t.context.argv = getDefaultArgv();
@@ -200,18 +200,14 @@ test.serial("ui5 cache clean: removes both entries and reports", async (t) => {
200200
t.true(allOutput.includes("Checking cache at"), "Prints checking line");
201201
t.true(allOutput.includes(TEST_UI5_DATA_DIR), "Shows resolved ui5DataDir");
202202

203-
// Absolute paths in listing
204-
const expectedFrameworkAbs = path.join(TEST_UI5_DATA_DIR, "framework");
205-
const expectedBuildAbs = path.join(TEST_UI5_DATA_DIR, "buildCache/v0_7");
206-
t.true(allOutput.includes(expectedFrameworkAbs), "Shows absolute framework path");
207-
t.true(allOutput.includes(expectedBuildAbs), "Shows absolute build cache path");
203+
// Absolute paths
204+
t.true(allOutput.includes(path.join(TEST_UI5_DATA_DIR, "framework")), "Shows absolute framework path");
205+
t.true(allOutput.includes(path.join(TEST_UI5_DATA_DIR, "buildCache/v0_7")), "Shows absolute build path");
208206

209-
// Framework detail: projects, libraries, versions
210-
t.true(allOutput.includes("2 projects"), "Shows project count");
211-
t.true(allOutput.includes("18 libraries"), "Shows library count");
212-
t.true(allOutput.includes("5 versions"), "Shows version count");
207+
// New format: "5 versions of 18 libraries"
208+
t.true(allOutput.includes("5 versions of 18 libraries"), "Shows new library stats format");
213209

214-
// Build cache detail: pre-clean size reused (not VACUUM-freed 7 MB)
210+
// Build cache size — pre-clean size reused (not VACUUM-freed 7 MB)
215211
t.true(allOutput.includes("8.0 MB"), "Shows pre-clean build cache size");
216212
t.false(allOutput.includes("7.0 MB"), "Does not show VACUUM-freed size");
217213

@@ -240,43 +236,54 @@ test.serial("ui5 cache clean: user cancels", async (t) => {
240236
t.false(allOutput.includes("Success"), "Does not show success message");
241237
});
242238

243-
test.serial("ui5 cache clean: framework only — singular labels", async (t) => {
239+
test.serial("ui5 cache clean: framework only — formats library stats correctly", async (t) => {
244240
const {cache, argv, stderrWriteStub, frameworkCacheCleanCache, frameworkCacheGetCacheInfo,
245241
buildCacheGetCacheInfo, yesnoStub} = t.context;
246242

247-
const singleStub = {path: "framework", projectCount: 1, libraryCount: 1, versionCount: 1};
248-
frameworkCacheGetCacheInfo.resolves(singleStub);
243+
// Plural
244+
frameworkCacheGetCacheInfo.resolves(FRAMEWORK_STUB);
249245
buildCacheGetCacheInfo.resolves(null);
250246
yesnoStub.resolves(true);
251-
frameworkCacheCleanCache.resolves(singleStub);
247+
frameworkCacheCleanCache.resolves(FRAMEWORK_STUB);
252248

253249
argv["_"] = ["cache", "clean"];
254250
await cache.handler(argv);
255251

256-
const allOutput = stderrWriteStub.args.map((a) => a[0]).join("");
257-
t.true(allOutput.includes("1 project,"), "Uses singular 'project'");
258-
t.true(allOutput.includes("1 library,"), "Uses singular 'library'");
259-
t.true(allOutput.includes("1 version"), "Uses singular 'version'");
252+
let allOutput = stderrWriteStub.args.map((a) => a[0]).join("");
253+
t.true(allOutput.includes("5 versions of 18 libraries"), "Shows plural format");
260254
t.false(allOutput.includes("Build cache (DB)"), "Does not mention build cache");
261-
t.true(allOutput.includes("Cleaned UI5 Framework packages"), "Success mentions framework only");
255+
256+
// Singular — reset stubs
257+
stderrWriteStub.resetHistory();
258+
const singleStub = {path: "framework", libraryCount: 1, versionCount: 1};
259+
frameworkCacheGetCacheInfo.resetBehavior();
260+
frameworkCacheCleanCache.resetBehavior();
261+
frameworkCacheGetCacheInfo.resolves(singleStub);
262+
frameworkCacheCleanCache.resolves(singleStub);
263+
264+
argv["yes"] = true;
265+
await cache.handler(argv);
266+
267+
allOutput = stderrWriteStub.args.map((a) => a[0]).join("");
268+
t.true(allOutput.includes("1 version of 1 library"), "Uses singular 'version' and 'library'");
262269
});
263270

264-
test.serial("ui5 cache clean: framework only — plural labels", async (t) => {
271+
test.serial("ui5 cache clean: thousands separator in library stats", async (t) => {
265272
const {cache, argv, stderrWriteStub, frameworkCacheCleanCache, frameworkCacheGetCacheInfo,
266273
buildCacheGetCacheInfo, yesnoStub} = t.context;
267274

268-
frameworkCacheGetCacheInfo.resolves(FRAMEWORK_STUB);
275+
const largeStub = {path: "framework", libraryCount: 155, versionCount: 1189};
276+
frameworkCacheGetCacheInfo.resolves(largeStub);
269277
buildCacheGetCacheInfo.resolves(null);
270278
yesnoStub.resolves(true);
271-
frameworkCacheCleanCache.resolves(FRAMEWORK_STUB);
279+
frameworkCacheCleanCache.resolves(largeStub);
272280

273281
argv["_"] = ["cache", "clean"];
274282
await cache.handler(argv);
275283

276284
const allOutput = stderrWriteStub.args.map((a) => a[0]).join("");
277-
t.true(allOutput.includes("2 projects"), "Uses plural 'projects'");
278-
t.true(allOutput.includes("18 libraries"), "Uses plural 'libraries'");
279-
t.true(allOutput.includes("5 versions"), "Uses plural 'versions'");
285+
t.true(allOutput.includes("1,189 versions of 155 libraries"),
286+
"Shows thousands separator for large counts");
280287
});
281288

282289
test.serial("ui5 cache clean: build only", async (t) => {

0 commit comments

Comments
 (0)