Skip to content
Merged
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
51 changes: 12 additions & 39 deletions packages/rstack/src/fmt/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ensureProjectCacheDir } from '../projectCache.ts';
import { fmtCacheFileName } from './cacheStore.ts';
import { resolveFmtConfig } from './config.ts';
import { discoverFmtFiles } from './discovery.ts';
import { formatDuration } from './duration.ts';
import { createRelativePathResolver, toPosixPath } from './pathHelpers.ts';
import { runFmtFiles } from './runner.ts';
import type { FmtMode, FmtRunResult, ResolvedFmtConfig } from './types.ts';
Expand Down Expand Up @@ -157,35 +158,6 @@ const createDisplayPathResolver = (
return (filePath) => toPosixPath(resolveRelativePath(filePath));
};

const prettyTime = (seconds: number): string => {
const format = (time: string, unit: 'm' | 's') =>
color.bold(`${time}${unit}`);

if (seconds < 10) {
const digits = seconds >= 0.01 ? 2 : 3;
return format(seconds.toFixed(digits), 's');
}

if (seconds < 60) {
return format(seconds.toFixed(1), 's');
}

const minutes = Math.floor(seconds / 60);
const minutesLabel = format(minutes.toFixed(0), 'm');
const remainingSeconds = seconds % 60;

if (remainingSeconds === 0) {
return minutesLabel;
}

const secondsLabel = format(
remainingSeconds.toFixed(remainingSeconds % 1 === 0 ? 0 : 1),
's',
);

return `${minutesLabel} ${secondsLabel}`;
};

const formatCount = (count: number): string => color.bold(count);
const formatFileCount = (count: number, isError = false): string => {
const formattedCount = formatCount(count);
Expand All @@ -207,7 +179,7 @@ const logFmtResult = (
mode: FmtMode,
cwd: string,
processedFileCount: number,
durationSeconds: number,
durationMilliseconds: number,
fixCommand?: string,
): void => {
let writtenCount = 0;
Expand All @@ -229,13 +201,18 @@ const logFmtResult = (
}
}

if (mode === 'list-different') {
return;
}

const time = color.bold(formatDuration(durationMilliseconds));

if (mode === 'write') {
if (writtenCount === 0 && result.exitCode !== 0) {
return;
}

const processedFiles = formatFileCount(processedFileCount);
const time = prettyTime(durationSeconds);
const message =
writtenCount > 0
? `Formatted ${formatCount(writtenCount)} of ${processedFiles} in ${time}.`
Expand All @@ -244,21 +221,17 @@ const logFmtResult = (
return;
}

if (mode !== 'check') {
return;
}

if (differentCount > 0) {
const differentFiles = formatFileCount(differentCount, true);
const processedFiles = formatFileCount(processedFileCount);
const fixHint = fixCommand
? `Run ${color.cyan(fixCommand)} to fix.`
: `Rerun this command without ${color.cyan('--check')} to fix.`;
logger.error(`Formatting issues found in ${differentFiles}. ${fixHint}`);
logger.info(`Checked ${processedFiles} in ${prettyTime(durationSeconds)}.`);
logger.info(`Checked ${processedFiles} in ${time}.`);
} else if (result.exitCode === 0) {
logger.success(
`Checked ${formatFileCount(processedFileCount)} in ${prettyTime(durationSeconds)}. No issues found.`,
`Checked ${formatFileCount(processedFileCount)} in ${time}. No issues found.`,
);
}
};
Expand Down Expand Up @@ -415,13 +388,13 @@ const runFmtCLI = async (
return;
}

const durationSeconds = (performance.now() - startTime) / 1000;
const durationMilliseconds = performance.now() - startTime;
logFmtResult(
result,
mode,
cwd,
result.processedFileCount,
durationSeconds,
durationMilliseconds,
fixCommand,
);
process.exitCode = result.exitCode;
Expand Down
34 changes: 34 additions & 0 deletions packages/rstack/src/fmt/duration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/** Formats sub-second durations in milliseconds and preserves the existing longer-duration format. */
const formatDuration = (milliseconds: number): string => {
if (milliseconds < 1) {
return '<1ms';
}

const roundedMilliseconds = Math.round(milliseconds);
if (roundedMilliseconds < 1000) {
return `${roundedMilliseconds}ms`;
}

const seconds = milliseconds / 1000;
if (seconds < 10) {
return `${seconds.toFixed(2)}s`;
}

if (seconds < 60) {
return `${seconds.toFixed(1)}s`;
}

const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;

if (remainingSeconds === 0) {
return `${minutes}m`;
}

const secondsLabel = remainingSeconds.toFixed(
remainingSeconds % 1 === 0 ? 0 : 1,
);
return `${minutes}m ${secondsLabel}s`;
};

export { formatDuration };
5 changes: 4 additions & 1 deletion packages/rstack/tests/cli/fmt/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ export const createCliEnv = (): NodeJS.ProcessEnv => {
};

export const normalizeDuration = (output: string): string =>
output.replace(/\d+m(?: \d+(?:\.\d+)?s)?|\d+(?:\.\d+)?s/g, '<duration>');
output.replace(
/<1ms|\d+ms|\d+m(?: \d+(?:\.\d+)?s)?|\d+(?:\.\d+)?s/g,
'<duration>',
);

export const expectWriteSummary = (
output: string,
Expand Down
19 changes: 19 additions & 0 deletions packages/rstack/tests/fmt/duration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { expect, test } from 'rstack/test';
import { formatDuration } from '../../src/fmt/duration.ts';

test.each([
[0, '<1ms'],
[0.999, '<1ms'],
[1, '1ms'],
[29.6, '30ms'],
[999.4, '999ms'],
[999.6, '1.00s'],
[1_390, '1.39s'],
[1_234, '1.23s'],
[12_340, '12.3s'],
[60_000, '1m'],
[60_123, '1m 0.1s'],
[3_661_234, '61m 1.2s'],
] as const)('formats %sms as %s', (milliseconds, expected) => {
expect(formatDuration(milliseconds)).toBe(expected);
});