Skip to content

Commit c46046f

Browse files
feat(utils): add file size logging (#65)
This PR includes: - formatBytes function and tests - logPersisted files function and tests - log the files in the CLI `collect` command closes #59 --------- Co-authored-by: Matěj Chalk <[email protected]>
1 parent 7438d1c commit c46046f

File tree

8 files changed

+687
-12
lines changed

8 files changed

+687
-12
lines changed

package-lock.json

+525-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+3
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@
5252
"vitest": "~0.32.0"
5353
},
5454
"optionalDependencies": {
55+
"@esbuild/darwin-arm64": "^0.19.4",
56+
"@nx/nx-darwin-arm64": "^16.9.1",
57+
"@nx/nx-darwin-x64": "^16.9.1",
5558
"@nx/nx-linux-x64-gnu": "16.7.4"
5659
},
5760
"config": {

packages/cli/src/lib/collect/command-object.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
CollectOptions,
55
CollectOutputError,
66
persistReport,
7+
logPersistedResults,
78
} from '@quality-metrics/utils';
89
import { CommandModule } from 'yargs';
910
import * as packageJson from '../../../package.json';
@@ -19,7 +20,9 @@ export function yargsCollectCommandObject() {
1920
version: packageJson.version,
2021
};
2122

22-
await persistReport(report, config);
23+
const persistResults = await persistReport(report, config);
24+
25+
logPersistedResults(persistResults);
2326

2427
// validate report
2528
report.plugins.forEach(plugin => {

packages/utils/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ export {
1515
persistReport,
1616
PersistDirError,
1717
PersistError,
18+
logPersistedResults,
1819
} from './lib/collect/implementation/persist';

packages/utils/src/lib/collect/implementation/persist.spec.ts

+56-6
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
2+
import { logPersistedResults, persistReport } from './persist';
3+
import { readFileSync, unlinkSync } from 'fs';
14
import { Report } from '@quality-metrics/models';
25
import {
3-
MEMFS_VOLUME,
46
dummyConfig,
57
dummyReport,
8+
MEMFS_VOLUME,
69
mockPersistConfig,
710
} from '@quality-metrics/models/testing';
8-
import { readFileSync, unlinkSync } from 'fs';
911
import { vol } from 'memfs';
1012
import { join } from 'path';
11-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
1213
import { mockConsole, unmockConsole } from './mock/helper.mock';
13-
import { persistReport } from './persist';
1414

1515
vi.mock('fs', async () => {
1616
const memfs: typeof import('memfs') = await vi.importActual('memfs');
@@ -35,10 +35,9 @@ const readReport = (format: 'json' | 'md') => {
3535
};
3636

3737
const config = dummyConfig(MEMFS_VOLUME);
38+
let logs: string[] = [];
3839

3940
describe('persistReport', () => {
40-
let logs: string[] = [];
41-
4241
beforeEach(async () => {
4342
vol.reset();
4443
vol.fromJSON(
@@ -143,3 +142,54 @@ describe('persistReport', () => {
143142
// TODO: should throw PersistDirError
144143
// TODO: should throw PersistError
145144
});
145+
146+
describe('logPersistedResults', () => {
147+
beforeEach(async () => {
148+
vol.reset();
149+
vol.fromJSON(
150+
{
151+
[reportPath('json')]: '',
152+
[reportPath('md')]: '',
153+
},
154+
MEMFS_VOLUME,
155+
);
156+
unlinkSync(reportPath('json'));
157+
unlinkSync(reportPath('md'));
158+
159+
logs = [];
160+
mockConsole(msg => logs.push(msg));
161+
});
162+
163+
afterEach(() => {
164+
logs = [];
165+
unmockConsole();
166+
});
167+
168+
it('should log report sizes correctly`', async () => {
169+
logPersistedResults([{ status: 'fulfilled', value: ['out.json', 10000] }]);
170+
expect(logs.length).toBe(2);
171+
expect(logs).toContain('Generated reports successfully: ');
172+
expect(logs).toContain('- out.json (9.77 kB)');
173+
});
174+
175+
it('should log fails correctly`', async () => {
176+
logPersistedResults([{ status: 'rejected', reason: 'fail' }]);
177+
expect(logs.length).toBe(2);
178+
179+
expect(logs).toContain('Generated reports failed: ');
180+
expect(logs).toContain('- fail');
181+
});
182+
183+
it('should log report sizes and fails correctly`', async () => {
184+
logPersistedResults([
185+
{ status: 'fulfilled', value: ['out.json', 10000] },
186+
{ status: 'rejected', reason: 'fail' },
187+
]);
188+
expect(logs.length).toBe(4);
189+
expect(logs).toContain('Generated reports successfully: ');
190+
expect(logs).toContain('- out.json (9.77 kB)');
191+
192+
expect(logs).toContain('Generated reports failed: ');
193+
expect(logs).toContain('- fail');
194+
});
195+
});

packages/utils/src/lib/collect/implementation/persist.ts

+39-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { existsSync, mkdirSync } from 'fs';
2-
import { writeFile } from 'fs/promises';
2+
import { writeFile, stat } from 'fs/promises';
33
import { join } from 'path';
4+
import chalk from 'chalk';
45
import { CoreConfig, Report } from '@quality-metrics/models';
6+
import { formatBytes } from './utils';
57
import { reportToStdout } from './report-to-stdout';
68
import { reportToMd } from './report-to-md';
79

@@ -17,7 +19,12 @@ export class PersistError extends Error {
1719
}
1820
}
1921

20-
export async function persistReport(report: Report, config: CoreConfig) {
22+
export type PersistResult = PromiseSettledResult<readonly [string, number]>[];
23+
24+
export async function persistReport(
25+
report: Report,
26+
config: CoreConfig,
27+
): Promise<PersistResult> {
2128
const { persist } = config;
2229
const outputPath = persist.outputPath;
2330
let { format } = persist;
@@ -54,7 +61,8 @@ export async function persistReport(report: Report, config: CoreConfig) {
5461
return (
5562
writeFile(reportPath, content)
5663
// return reportPath instead of void
57-
.then(() => reportPath)
64+
.then(() => stat(reportPath))
65+
.then(stats => [reportPath, stats.size] as const)
5866
.catch(e => {
5967
console.warn(e);
6068
throw new PersistError(reportPath);
@@ -63,3 +71,31 @@ export async function persistReport(report: Report, config: CoreConfig) {
6371
}),
6472
);
6573
}
74+
75+
export function logPersistedResults(persistResult: PersistResult) {
76+
const succeededPersistedResults = persistResult.filter(
77+
(result): result is PromiseFulfilledResult<[string, number]> =>
78+
result.status === 'fulfilled',
79+
);
80+
81+
if (succeededPersistedResults.length) {
82+
console.log(`Generated reports successfully: `);
83+
succeededPersistedResults.forEach(res => {
84+
const [fileName, size] = res.value;
85+
console.log(
86+
`- ${chalk.bold(fileName)} (${chalk.gray(formatBytes(size))})`,
87+
);
88+
});
89+
}
90+
91+
const failedPersistedResults = persistResult.filter(
92+
(result): result is PromiseRejectedResult => result.status === 'rejected',
93+
);
94+
95+
if (failedPersistedResults.length) {
96+
console.log(`Generated reports failed: `);
97+
failedPersistedResults.forEach(result => {
98+
console.log(`- ${chalk.bold(result.reason)}`);
99+
});
100+
}
101+
}

packages/utils/src/lib/collect/implementation/utils.spec.ts

+47-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect } from 'vitest';
2-
import { calcDuration, countWeightedRefs, sumRefs } from './utils';
2+
import { calcDuration, formatBytes, countWeightedRefs, sumRefs } from './utils';
33
import { CategoryConfig } from '@quality-metrics/models';
44

55
describe('calcDuration', () => {
@@ -60,3 +60,49 @@ describe('sumRefs', () => {
6060
expect(sumRefs(refs)).toBe(11);
6161
});
6262
});
63+
64+
describe('formatBytes', () => {
65+
it('should log file sizes in Bytes`', async () => {
66+
expect(formatBytes(1000)).toBe('1000 B');
67+
});
68+
69+
it('should log file sizes in KB`', async () => {
70+
expect(formatBytes(10000)).toBe('9.77 kB');
71+
});
72+
73+
it('should log file sizes in MB`', async () => {
74+
expect(formatBytes(10000000)).toBe('9.54 MB');
75+
});
76+
77+
it('should log file sizes in bytes`', async () => {
78+
expect(formatBytes(10000000000)).toBe('9.31 GB');
79+
});
80+
81+
it('should log file sizes in TB`', async () => {
82+
expect(formatBytes(10000000000000)).toBe('9.09 TB');
83+
});
84+
85+
it('should log file sizes in PB`', async () => {
86+
expect(formatBytes(10000000000000000)).toBe('8.88 PB');
87+
});
88+
89+
it('should log file sizes in EB`', async () => {
90+
expect(formatBytes(10000000000000000000)).toBe('8.67 EB');
91+
});
92+
93+
it('should log file sizes in ZB`', async () => {
94+
expect(formatBytes(10000000000000000000000)).toBe('8.47 ZB');
95+
});
96+
97+
it('should log file sizes in YB`', async () => {
98+
expect(formatBytes(10000000000000000000000000)).toBe('8.27 YB');
99+
});
100+
101+
it('should log file sizes correctly with correct decimal`', async () => {
102+
expect(formatBytes(10000, 1)).toBe('9.8 kB');
103+
});
104+
105+
it('should log file sizes of 0 if no size is given`', async () => {
106+
expect(formatBytes(0)).toBe('0 B');
107+
});
108+
});

packages/utils/src/lib/collect/implementation/utils.ts

+12
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@ export function countWeightedRefs(refs: CategoryConfig['refs']) {
1414
.reduce((sum, { weight }) => sum + weight, 0);
1515
}
1616

17+
export function formatBytes(bytes: number, decimals = 2) {
18+
if (!+bytes) return '0 B';
19+
20+
const k = 1024;
21+
const dm = decimals < 0 ? 0 : decimals;
22+
const sizes = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
23+
24+
const i = Math.floor(Math.log(bytes) / Math.log(k));
25+
26+
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
27+
}
28+
1729
export function calcDuration(start: number, stop?: number): number {
1830
stop = stop !== undefined ? stop : performance.now();
1931
return Math.floor(stop - start);

0 commit comments

Comments
 (0)