modestbench 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +131 -34
- package/dist/cli/commands/analyze.cjs +60 -0
- package/dist/cli/commands/analyze.cjs.map +1 -0
- package/dist/cli/commands/analyze.d.cts +35 -0
- package/dist/cli/commands/analyze.d.cts.map +1 -0
- package/dist/cli/commands/analyze.d.ts +35 -0
- package/dist/cli/commands/analyze.d.ts.map +1 -0
- package/dist/cli/commands/analyze.js +56 -0
- package/dist/cli/commands/analyze.js.map +1 -0
- package/dist/cli/commands/baseline.cjs +404 -0
- package/dist/cli/commands/baseline.cjs.map +1 -0
- package/dist/cli/commands/baseline.d.cts +72 -0
- package/dist/cli/commands/baseline.d.cts.map +1 -0
- package/dist/cli/commands/baseline.d.ts +72 -0
- package/dist/cli/commands/baseline.d.ts.map +1 -0
- package/dist/cli/commands/baseline.js +396 -0
- package/dist/cli/commands/baseline.js.map +1 -0
- package/dist/cli/commands/history.d.cts +1 -1
- package/dist/cli/commands/history.d.cts.map +1 -1
- package/dist/cli/commands/history.d.ts +1 -1
- package/dist/cli/commands/history.d.ts.map +1 -1
- package/dist/cli/commands/init.cjs +88 -155
- package/dist/cli/commands/init.cjs.map +1 -1
- package/dist/cli/commands/init.d.cts +4 -4
- package/dist/cli/commands/init.d.cts.map +1 -1
- package/dist/cli/commands/init.d.ts +4 -4
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +88 -155
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/run.cjs +132 -114
- package/dist/cli/commands/run.cjs.map +1 -1
- package/dist/cli/commands/run.d.cts +16 -3
- package/dist/cli/commands/run.d.cts.map +1 -1
- package/dist/cli/commands/run.d.ts +16 -3
- package/dist/cli/commands/run.d.ts.map +1 -1
- package/dist/cli/commands/run.js +131 -80
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/index.cjs +583 -394
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.d.cts +4 -16
- package/dist/cli/index.d.cts.map +1 -1
- package/dist/cli/index.d.ts +4 -16
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +575 -386
- package/dist/cli/index.js.map +1 -1
- package/dist/config/budget-schema.cjs +172 -0
- package/dist/config/budget-schema.cjs.map +1 -0
- package/dist/config/budget-schema.d.cts +59 -0
- package/dist/config/budget-schema.d.cts.map +1 -0
- package/dist/config/budget-schema.d.ts +59 -0
- package/dist/config/budget-schema.d.ts.map +1 -0
- package/dist/config/budget-schema.js +166 -0
- package/dist/config/budget-schema.js.map +1 -0
- package/dist/config/schema.cjs +182 -2
- package/dist/config/schema.cjs.map +1 -1
- package/dist/config/schema.d.cts +122 -3
- package/dist/config/schema.d.cts.map +1 -1
- package/dist/config/schema.d.ts +122 -3
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +180 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/constants.cjs +45 -2
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +41 -0
- package/dist/constants.d.cts.map +1 -1
- package/dist/constants.d.ts +41 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +44 -1
- package/dist/constants.js.map +1 -1
- package/dist/core/engine.cjs +103 -21
- package/dist/core/engine.cjs.map +1 -1
- package/dist/core/engine.d.cts +7 -7
- package/dist/core/engine.d.cts.map +1 -1
- package/dist/core/engine.d.ts +7 -7
- package/dist/core/engine.d.ts.map +1 -1
- package/dist/core/engine.js +104 -22
- package/dist/core/engine.js.map +1 -1
- package/dist/core/output-path-resolver.cjs +8 -1
- package/dist/core/output-path-resolver.cjs.map +1 -1
- package/dist/core/output-path-resolver.d.cts.map +1 -1
- package/dist/core/output-path-resolver.d.ts.map +1 -1
- package/dist/core/output-path-resolver.js +9 -2
- package/dist/core/output-path-resolver.js.map +1 -1
- package/dist/errors/base.cjs +12 -3
- package/dist/errors/base.cjs.map +1 -1
- package/dist/errors/base.d.cts +7 -0
- package/dist/errors/base.d.cts.map +1 -1
- package/dist/errors/base.d.ts +7 -0
- package/dist/errors/base.d.ts.map +1 -1
- package/dist/errors/base.js +10 -2
- package/dist/errors/base.js.map +1 -1
- package/dist/errors/budget.cjs +37 -0
- package/dist/errors/budget.cjs.map +1 -0
- package/dist/errors/budget.d.cts +31 -0
- package/dist/errors/budget.d.cts.map +1 -0
- package/dist/errors/budget.d.ts +31 -0
- package/dist/errors/budget.d.ts.map +1 -0
- package/dist/errors/budget.js +33 -0
- package/dist/errors/budget.js.map +1 -0
- package/dist/errors/index.cjs +4 -1
- package/dist/errors/index.cjs.map +1 -1
- package/dist/errors/index.d.cts +1 -0
- package/dist/errors/index.d.cts.map +1 -1
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +2 -0
- package/dist/errors/index.js.map +1 -1
- package/dist/index.cjs +13 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/reporters/csv.cjs +37 -17
- package/dist/reporters/csv.cjs.map +1 -1
- package/dist/reporters/csv.d.cts +3 -6
- package/dist/reporters/csv.d.cts.map +1 -1
- package/dist/reporters/csv.d.ts +3 -6
- package/dist/reporters/csv.d.ts.map +1 -1
- package/dist/reporters/csv.js +37 -17
- package/dist/reporters/csv.js.map +1 -1
- package/dist/reporters/human.cjs +66 -40
- package/dist/reporters/human.cjs.map +1 -1
- package/dist/reporters/human.d.cts +14 -13
- package/dist/reporters/human.d.cts.map +1 -1
- package/dist/reporters/human.d.ts +14 -13
- package/dist/reporters/human.d.ts.map +1 -1
- package/dist/reporters/human.js +66 -40
- package/dist/reporters/human.js.map +1 -1
- package/dist/reporters/json.cjs +23 -48
- package/dist/reporters/json.cjs.map +1 -1
- package/dist/reporters/json.d.cts +2 -28
- package/dist/reporters/json.d.cts.map +1 -1
- package/dist/reporters/json.d.ts +2 -28
- package/dist/reporters/json.d.ts.map +1 -1
- package/dist/reporters/json.js +25 -50
- package/dist/reporters/json.js.map +1 -1
- package/dist/reporters/profile-human.cjs +149 -0
- package/dist/reporters/profile-human.cjs.map +1 -0
- package/dist/reporters/profile-human.d.cts +44 -0
- package/dist/reporters/profile-human.d.cts.map +1 -0
- package/dist/reporters/profile-human.d.ts +44 -0
- package/dist/reporters/profile-human.d.ts.map +1 -0
- package/dist/reporters/profile-human.js +142 -0
- package/dist/reporters/profile-human.js.map +1 -0
- package/dist/reporters/simple.cjs +64 -44
- package/dist/reporters/simple.cjs.map +1 -1
- package/dist/reporters/simple.d.cts +14 -14
- package/dist/reporters/simple.d.cts.map +1 -1
- package/dist/reporters/simple.d.ts +14 -14
- package/dist/reporters/simple.d.ts.map +1 -1
- package/dist/reporters/simple.js +64 -44
- package/dist/reporters/simple.js.map +1 -1
- package/dist/schema/modestbench-config.schema.json +153 -0
- package/dist/services/baseline-storage.cjs +151 -0
- package/dist/services/baseline-storage.cjs.map +1 -0
- package/dist/services/baseline-storage.d.cts +55 -0
- package/dist/services/baseline-storage.d.cts.map +1 -0
- package/dist/services/baseline-storage.d.ts +55 -0
- package/dist/services/baseline-storage.d.ts.map +1 -0
- package/dist/services/baseline-storage.js +147 -0
- package/dist/services/baseline-storage.js.map +1 -0
- package/dist/services/budget-evaluator.cjs +146 -0
- package/dist/services/budget-evaluator.cjs.map +1 -0
- package/dist/services/budget-evaluator.d.cts +29 -0
- package/dist/services/budget-evaluator.d.cts.map +1 -0
- package/dist/services/budget-evaluator.d.ts +29 -0
- package/dist/services/budget-evaluator.d.ts.map +1 -0
- package/dist/services/budget-evaluator.js +142 -0
- package/dist/services/budget-evaluator.js.map +1 -0
- package/dist/services/config-manager.cjs +23 -9
- package/dist/services/config-manager.cjs.map +1 -1
- package/dist/services/config-manager.d.cts +6 -1
- package/dist/services/config-manager.d.cts.map +1 -1
- package/dist/services/config-manager.d.ts +6 -1
- package/dist/services/config-manager.d.ts.map +1 -1
- package/dist/services/config-manager.js +23 -9
- package/dist/services/config-manager.js.map +1 -1
- package/dist/services/file-loader.cjs +3 -6
- package/dist/services/file-loader.cjs.map +1 -1
- package/dist/services/file-loader.d.cts.map +1 -1
- package/dist/services/file-loader.d.ts.map +1 -1
- package/dist/services/file-loader.js +3 -6
- package/dist/services/file-loader.js.map +1 -1
- package/dist/services/profiler/profile-filter.cjs +113 -0
- package/dist/services/profiler/profile-filter.cjs.map +1 -0
- package/dist/services/profiler/profile-filter.d.cts +20 -0
- package/dist/services/profiler/profile-filter.d.cts.map +1 -0
- package/dist/services/profiler/profile-filter.d.ts +20 -0
- package/dist/services/profiler/profile-filter.d.ts.map +1 -0
- package/dist/services/profiler/profile-filter.js +109 -0
- package/dist/services/profiler/profile-filter.js.map +1 -0
- package/dist/services/profiler/profile-parser.cjs +139 -0
- package/dist/services/profiler/profile-parser.cjs.map +1 -0
- package/dist/services/profiler/profile-parser.d.cts +18 -0
- package/dist/services/profiler/profile-parser.d.cts.map +1 -0
- package/dist/services/profiler/profile-parser.d.ts +18 -0
- package/dist/services/profiler/profile-parser.d.ts.map +1 -0
- package/dist/services/profiler/profile-parser.js +132 -0
- package/dist/services/profiler/profile-parser.js.map +1 -0
- package/dist/services/profiler/profile-runner.cjs +90 -0
- package/dist/services/profiler/profile-runner.cjs.map +1 -0
- package/dist/services/profiler/profile-runner.d.cts +29 -0
- package/dist/services/profiler/profile-runner.d.cts.map +1 -0
- package/dist/services/profiler/profile-runner.d.ts +29 -0
- package/dist/services/profiler/profile-runner.d.ts.map +1 -0
- package/dist/services/profiler/profile-runner.js +86 -0
- package/dist/services/profiler/profile-runner.js.map +1 -0
- package/dist/services/reporter-registry.cjs +18 -24
- package/dist/services/reporter-registry.cjs.map +1 -1
- package/dist/services/reporter-registry.d.cts +18 -40
- package/dist/services/reporter-registry.d.cts.map +1 -1
- package/dist/services/reporter-registry.d.ts +18 -40
- package/dist/services/reporter-registry.d.ts.map +1 -1
- package/dist/services/reporter-registry.js +18 -24
- package/dist/services/reporter-registry.js.map +1 -1
- package/dist/types/budgets.cjs +8 -0
- package/dist/types/budgets.cjs.map +1 -0
- package/dist/types/budgets.d.cts +149 -0
- package/dist/types/budgets.d.cts.map +1 -0
- package/dist/types/budgets.d.ts +149 -0
- package/dist/types/budgets.d.ts.map +1 -0
- package/dist/types/budgets.js +7 -0
- package/dist/types/budgets.js.map +1 -0
- package/dist/types/cli.cjs +2 -11
- package/dist/types/cli.cjs.map +1 -1
- package/dist/types/cli.d.cts +3 -227
- package/dist/types/cli.d.cts.map +1 -1
- package/dist/types/cli.d.ts +3 -227
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/cli.js +2 -11
- package/dist/types/cli.js.map +1 -1
- package/dist/types/core.cjs +6 -1
- package/dist/types/core.cjs.map +1 -1
- package/dist/types/core.d.cts +13 -2
- package/dist/types/core.d.cts.map +1 -1
- package/dist/types/core.d.ts +13 -2
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/core.js +2 -1
- package/dist/types/core.js.map +1 -1
- package/dist/types/index.cjs +5 -0
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.d.cts +2 -0
- package/dist/types/index.d.cts.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/interfaces.d.cts +15 -8
- package/dist/types/interfaces.d.cts.map +1 -1
- package/dist/types/interfaces.d.ts +15 -8
- package/dist/types/interfaces.d.ts.map +1 -1
- package/dist/types/profiler.cjs +11 -0
- package/dist/types/profiler.cjs.map +1 -0
- package/dist/types/profiler.d.cts +100 -0
- package/dist/types/profiler.d.cts.map +1 -0
- package/dist/types/profiler.d.ts +100 -0
- package/dist/types/profiler.d.ts.map +1 -0
- package/dist/types/profiler.js +10 -0
- package/dist/types/profiler.js.map +1 -0
- package/dist/types/utility.cjs.map +1 -1
- package/dist/types/utility.d.cts +0 -8
- package/dist/types/utility.d.cts.map +1 -1
- package/dist/types/utility.d.ts +0 -8
- package/dist/types/utility.d.ts.map +1 -1
- package/dist/types/utility.js.map +1 -1
- package/dist/utils/identifiers.cjs +32 -0
- package/dist/utils/identifiers.cjs.map +1 -0
- package/dist/utils/identifiers.d.cts +32 -0
- package/dist/utils/identifiers.d.cts.map +1 -0
- package/dist/utils/identifiers.d.ts +32 -0
- package/dist/utils/identifiers.d.ts.map +1 -0
- package/dist/utils/identifiers.js +27 -0
- package/dist/utils/identifiers.js.map +1 -0
- package/dist/utils/package.cjs +40 -0
- package/dist/utils/package.cjs.map +1 -0
- package/dist/utils/package.d.cts +15 -0
- package/dist/utils/package.d.cts.map +1 -0
- package/dist/utils/package.d.ts +15 -0
- package/dist/utils/package.d.ts.map +1 -0
- package/dist/utils/package.js +33 -0
- package/dist/utils/package.js.map +1 -0
- package/dist/utils/type-guards.cjs +48 -0
- package/dist/utils/type-guards.cjs.map +1 -0
- package/dist/utils/type-guards.d.cts +22 -0
- package/dist/utils/type-guards.d.cts.map +1 -0
- package/dist/utils/type-guards.d.ts +22 -0
- package/dist/utils/type-guards.d.ts.map +1 -0
- package/dist/utils/type-guards.js +43 -0
- package/dist/utils/type-guards.js.map +1 -0
- package/package.json +10 -10
- package/src/cli/commands/analyze.ts +101 -0
- package/src/cli/commands/baseline.ts +577 -0
- package/src/cli/commands/history.ts +1 -1
- package/src/cli/commands/init.ts +105 -183
- package/src/cli/commands/run.ts +167 -98
- package/src/cli/index.ts +425 -183
- package/src/config/budget-schema.ts +189 -0
- package/src/config/schema.ts +260 -1
- package/src/constants.ts +53 -1
- package/src/core/engine.ts +151 -20
- package/src/core/output-path-resolver.ts +10 -2
- package/src/errors/base.ts +11 -2
- package/src/errors/budget.ts +38 -0
- package/src/errors/index.ts +3 -0
- package/src/index.ts +9 -0
- package/src/reporters/csv.ts +54 -25
- package/src/reporters/human.ts +88 -47
- package/src/reporters/json.ts +26 -71
- package/src/reporters/profile-human.ts +204 -0
- package/src/reporters/simple.ts +84 -53
- package/src/services/baseline-storage.ts +199 -0
- package/src/services/budget-evaluator.ts +182 -0
- package/src/services/config-manager.ts +23 -8
- package/src/services/file-loader.ts +3 -6
- package/src/services/profiler/profile-filter.ts +143 -0
- package/src/services/profiler/profile-parser.ts +194 -0
- package/src/services/profiler/profile-runner.ts +121 -0
- package/src/services/reporter-registry.ts +46 -81
- package/src/types/budgets.ts +180 -0
- package/src/types/cli.ts +5 -238
- package/src/types/core.ts +50 -10
- package/src/types/index.ts +5 -0
- package/src/types/interfaces.ts +16 -6
- package/src/types/profiler.ts +132 -0
- package/src/types/utility.ts +0 -10
- package/src/utils/identifiers.ts +58 -0
- package/src/utils/package.ts +35 -0
- package/src/utils/type-guards.ts +51 -0
package/src/reporters/human.ts
CHANGED
|
@@ -9,6 +9,7 @@ import path from 'node:path';
|
|
|
9
9
|
|
|
10
10
|
import type {
|
|
11
11
|
BenchmarkRun,
|
|
12
|
+
BudgetSummary,
|
|
12
13
|
FileResult,
|
|
13
14
|
ProgressState,
|
|
14
15
|
SuiteResult,
|
|
@@ -71,6 +72,84 @@ export class HumanReporter extends BaseReporter {
|
|
|
71
72
|
this.showProgress = options.progress ?? true;
|
|
72
73
|
}
|
|
73
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Format bytes in human-readable format
|
|
77
|
+
*/
|
|
78
|
+
private static formatBytes(this: void, bytes: number): string {
|
|
79
|
+
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
80
|
+
let size = bytes;
|
|
81
|
+
let unitIndex = 0;
|
|
82
|
+
|
|
83
|
+
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
84
|
+
size /= 1024;
|
|
85
|
+
unitIndex++;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return `${size.toFixed(1)} ${units[unitIndex]}`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Format file path - show relative path if within CWD, otherwise absolute
|
|
93
|
+
*/
|
|
94
|
+
private static formatPath(this: void, filePath: string): string {
|
|
95
|
+
const cwd = process.cwd();
|
|
96
|
+
const absolutePath = path.resolve(filePath);
|
|
97
|
+
|
|
98
|
+
// Check if the file is within the current working directory
|
|
99
|
+
if (absolutePath.startsWith(cwd + path.sep) || absolutePath === cwd) {
|
|
100
|
+
return path.relative(cwd, absolutePath);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return absolutePath;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Simple pluralization helper
|
|
108
|
+
*/
|
|
109
|
+
private static pluralize(this: void, str: string, count: number): string {
|
|
110
|
+
return count === 1 ? str : `${str}s`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
onBudgetResult(summary: BudgetSummary): void {
|
|
114
|
+
if (summary.total === 0 || this.quiet) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
this.clearProgress();
|
|
119
|
+
|
|
120
|
+
this.printLine();
|
|
121
|
+
const budgetHeader = `${this.colorize('magenta', ansiChars.block.full.repeat(2))} ${this.colorize('brightWhite', this.colorize('bold', 'Performance Budgets'))}`;
|
|
122
|
+
this.printLine(budgetHeader);
|
|
123
|
+
this.printLine();
|
|
124
|
+
|
|
125
|
+
for (const result of summary.results) {
|
|
126
|
+
const icon = result.passed ? ansiChars.checkmark : ansiChars.cross;
|
|
127
|
+
const iconColor = result.passed ? 'brightCyan' : 'brightRed';
|
|
128
|
+
|
|
129
|
+
this.printLine(
|
|
130
|
+
` ${this.colorize(iconColor, icon)} ${this.colorize('white', result.taskId)}`,
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
if (!result.passed && result.violations.length > 0) {
|
|
134
|
+
for (const violation of result.violations) {
|
|
135
|
+
this.printLine(
|
|
136
|
+
` ${this.colorize('brightRed', violation.message)}`,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
this.printLine();
|
|
143
|
+
|
|
144
|
+
const statusText =
|
|
145
|
+
summary.failed === 0
|
|
146
|
+
? `${this.colorize('brightCyan', ansiChars.checkmark)} All ${summary.total} budget(s) passed`
|
|
147
|
+
: `${this.colorize('brightRed', ansiChars.cross)} ${summary.failed} of ${summary.total} budget(s) failed`;
|
|
148
|
+
|
|
149
|
+
this.printLine(` ${statusText}`);
|
|
150
|
+
this.printLine();
|
|
151
|
+
}
|
|
152
|
+
|
|
74
153
|
onEnd(run: BenchmarkRun): void {
|
|
75
154
|
if (this.quiet) {
|
|
76
155
|
return;
|
|
@@ -121,7 +200,7 @@ export class HumanReporter extends BaseReporter {
|
|
|
121
200
|
);
|
|
122
201
|
}
|
|
123
202
|
this.printLine(
|
|
124
|
-
`${this.colorize('cyan', ansiChars.approx + ' Duration:')} ${this.colorize('brightWhite',
|
|
203
|
+
`${this.colorize('cyan', ansiChars.approx + ' Duration:')} ${this.colorize('brightWhite', BaseReporter.formatDuration(duration * 1000000))}`,
|
|
125
204
|
);
|
|
126
205
|
this.printLine();
|
|
127
206
|
|
|
@@ -135,7 +214,7 @@ export class HumanReporter extends BaseReporter {
|
|
|
135
214
|
this.printLine();
|
|
136
215
|
|
|
137
216
|
for (const failure of this.failures) {
|
|
138
|
-
const displayPath =
|
|
217
|
+
const displayPath = HumanReporter.formatPath(failure.file);
|
|
139
218
|
this.printLine(
|
|
140
219
|
` ${this.colorize('dim', displayPath)} ${this.colorize('dim', '›')} ${this.colorize('white', failure.suite)} ${this.colorize('dim', '›')} ${this.colorize('brightWhite', failure.task)}`,
|
|
141
220
|
);
|
|
@@ -189,7 +268,7 @@ export class HumanReporter extends BaseReporter {
|
|
|
189
268
|
);
|
|
190
269
|
} else {
|
|
191
270
|
this.printLine(
|
|
192
|
-
` ${this.colorize('magenta', ansiChars.checkmark)} ${totalPassed > 1 ? this.colorize('brightMagenta', 'All ') : ''}${this.colorize('bold', this.colorize('brightMagenta', `${totalPassed}`))} ${this.colorize('brightMagenta', `${
|
|
271
|
+
` ${this.colorize('magenta', ansiChars.checkmark)} ${totalPassed > 1 ? this.colorize('brightMagenta', 'All ') : ''}${this.colorize('bold', this.colorize('brightMagenta', `${totalPassed}`))} ${this.colorize('brightMagenta', `${HumanReporter.pluralize('task', totalPassed)} passed`)}`,
|
|
193
272
|
);
|
|
194
273
|
}
|
|
195
274
|
|
|
@@ -203,7 +282,7 @@ export class HumanReporter extends BaseReporter {
|
|
|
203
282
|
return;
|
|
204
283
|
}
|
|
205
284
|
|
|
206
|
-
const displayPath =
|
|
285
|
+
const displayPath = HumanReporter.formatPath(file);
|
|
207
286
|
const fileMarker = `${colors.magenta}${ansiChars.block.dark}${ansiChars.block.dark}${colors.reset}`;
|
|
208
287
|
this.printLine(
|
|
209
288
|
`${fileMarker} ${colors.underline}${this.colorize('brightMagenta', this.colorize('bold', displayPath))}${colors.reset}`,
|
|
@@ -278,7 +357,7 @@ export class HumanReporter extends BaseReporter {
|
|
|
278
357
|
\x1b[48;5;0m \x1b[48;5;14m \x1b[38;5;30;48;5;38m▄\x1b[38;5;14;48;5;14m▄\x1b[48;5;14m \x1b[38;5;45;48;5;14m▄\x1b[38;5;89;48;5;14m▄\x1b[38;5;89;48;5;89m▄\x1b[38;5;14;48;5;31m▄\x1b[48;5;14m \x1b[38;5;37;48;5;89m▄\x1b[48;5;198m \x1b[38;5;198;48;5;198m▄\x1b[38;5;31;48;5;14m▄\x1b[48;5;14m \x1b[48;5;0m \x1b[m \x1b[2mnode.js:\x1b[m \x1b[36m${run.environment.nodeVersion} \x1b[m
|
|
279
358
|
\x1b[48;5;0m \x1b[48;5;14m \x1b[38;5;44;48;5;31m▄\x1b[48;5;14m \x1b[38;5;126;48;5;38m▄\x1b[38;5;198;48;5;237m▄\x1b[38;5;237;48;5;37m▄\x1b[48;5;14m \x1b[38;5;14;48;5;14m▄\x1b[38;5;162;48;5;198m▄▄\x1b[38;5;53;48;5;240m▄\x1b[48;5;14m \x1b[48;5;0m \x1b[m \x1b[2mplatform:\x1b[m \x1b[36m${run.environment.platform} ${run.environment.arch} \x1b[m
|
|
280
359
|
\x1b[48;5;0m \x1b[38;5;45;48;5;14m▄\x1b[48;5;14m \x1b[38;5;14;48;5;37m▄\x1b[38;5;14;48;5;5m▄\x1b[38;5;14;48;5;44m▄\x1b[48;5;14m \x1b[38;5;45;48;5;14m▄\x1b[48;5;0m \x1b[m \x1b[2mcpu:\x1b[m \x1b[36m${run.environment.cpu.model} \x1b[2m(\x1b[m\x1b[36m${run.environment.cpu.cores} cores\x1b[2m) \x1b[m
|
|
281
|
-
\x1b[49;38;5;0m▀▀\x1b[38;5;0;48;5;6m▄\x1b[38;5;232;48;5;14m▄\x1b[38;5;38;48;5;14m▄\x1b[48;5;14m \x1b[38;5;30;48;5;14m▄\x1b[38;5;0;48;5;14m▄\x1b[38;5;0;48;5;23m▄\x1b[49;38;5;0m▀▀\x1b[m \x1b[2mmem:\x1b[m \x1b[36m${
|
|
360
|
+
\x1b[49;38;5;0m▀▀\x1b[38;5;0;48;5;6m▄\x1b[38;5;232;48;5;14m▄\x1b[38;5;38;48;5;14m▄\x1b[48;5;14m \x1b[38;5;30;48;5;14m▄\x1b[38;5;0;48;5;14m▄\x1b[38;5;0;48;5;23m▄\x1b[49;38;5;0m▀▀\x1b[m \x1b[2mmem:\x1b[m \x1b[36m${HumanReporter.formatBytes(run.environment.memory.total)} \x1b[m
|
|
282
361
|
\x1b[49m \x1b[49;38;5;0m▀\x1b[38;5;0;48;5;236m▄\x1b[38;5;0;48;5;45m▄\x1b[38;5;23;48;5;14m▄\x1b[48;5;14m \x1b[38;5;236;48;5;14m▄\x1b[38;5;0;48;5;44m▄\x1b[38;5;0;48;5;232m▄\x1b[49;38;5;0m▀\x1b[49m \x1b[m
|
|
283
362
|
\x1b[49m \x1b[49;38;5;0m▀▀\x1b[38;5;0;48;5;37m▄\x1b[38;5;0;48;5;14m▄\x1b[38;5;0;48;5;30m▄\x1b[49;38;5;0m▀▀\x1b[49m \x1b[m
|
|
284
363
|
`;
|
|
@@ -333,7 +412,7 @@ export class HumanReporter extends BaseReporter {
|
|
|
333
412
|
);
|
|
334
413
|
} else {
|
|
335
414
|
this.printLine(
|
|
336
|
-
` ${this.colorize('magenta', ansiChars.checkmark)} ${this.colorize('bold', this.colorize('brightWhite', `${passed}`))} ${this.colorize('brightWhite', `${
|
|
415
|
+
` ${this.colorize('magenta', ansiChars.checkmark)} ${this.colorize('bold', this.colorize('brightWhite', `${passed}`))} ${this.colorize('brightWhite', `${HumanReporter.pluralize('task', passed)} passed`)}`,
|
|
337
416
|
);
|
|
338
417
|
}
|
|
339
418
|
this.printLine();
|
|
@@ -414,37 +493,6 @@ export class HumanReporter extends BaseReporter {
|
|
|
414
493
|
return `${colors[color]}${text}${colors.reset}`;
|
|
415
494
|
}
|
|
416
495
|
|
|
417
|
-
/**
|
|
418
|
-
* Format bytes in human-readable format
|
|
419
|
-
*/
|
|
420
|
-
private formatBytes(bytes: number): string {
|
|
421
|
-
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
422
|
-
let size = bytes;
|
|
423
|
-
let unitIndex = 0;
|
|
424
|
-
|
|
425
|
-
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
426
|
-
size /= 1024;
|
|
427
|
-
unitIndex++;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
return `${size.toFixed(1)} ${units[unitIndex]}`;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
/**
|
|
434
|
-
* Format file path - show relative path if within CWD, otherwise absolute
|
|
435
|
-
*/
|
|
436
|
-
private formatPath(filePath: string): string {
|
|
437
|
-
const cwd = process.cwd();
|
|
438
|
-
const absolutePath = path.resolve(filePath);
|
|
439
|
-
|
|
440
|
-
// Check if the file is within the current working directory
|
|
441
|
-
if (absolutePath.startsWith(cwd + path.sep) || absolutePath === cwd) {
|
|
442
|
-
return path.relative(cwd, absolutePath);
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
return absolutePath;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
496
|
/**
|
|
449
497
|
* Format duration in human-readable format for progress display
|
|
450
498
|
*/
|
|
@@ -471,13 +519,6 @@ export class HumanReporter extends BaseReporter {
|
|
|
471
519
|
return str.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
472
520
|
}
|
|
473
521
|
|
|
474
|
-
/**
|
|
475
|
-
* Simple pluralization helper
|
|
476
|
-
*/
|
|
477
|
-
private pluralize(str: string, count: number): string {
|
|
478
|
-
return count === 1 ? str : `${str}s`;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
522
|
/**
|
|
482
523
|
* Print all task results in a suite with aligned columns
|
|
483
524
|
*/
|
|
@@ -534,9 +575,9 @@ export class HumanReporter extends BaseReporter {
|
|
|
534
575
|
};
|
|
535
576
|
}
|
|
536
577
|
|
|
537
|
-
const duration =
|
|
538
|
-
const opsPerSec =
|
|
539
|
-
const rme =
|
|
578
|
+
const duration = BaseReporter.formatDuration(result.mean); // already in nanoseconds
|
|
579
|
+
const opsPerSec = BaseReporter.formatOpsPerSecond(result.opsPerSecond);
|
|
580
|
+
const rme = BaseReporter.formatPercentage(result.marginOfError * 100);
|
|
540
581
|
|
|
541
582
|
return {
|
|
542
583
|
durationLen: this.getVisibleLength(duration),
|
package/src/reporters/json.ts
CHANGED
|
@@ -5,16 +5,11 @@
|
|
|
5
5
|
* processing, CI/CD integration, and data analysis.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
9
|
-
import { dirname } from 'node:path';
|
|
8
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
9
|
+
import { dirname, join } from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
10
11
|
|
|
11
|
-
import type {
|
|
12
|
-
BenchmarkRun,
|
|
13
|
-
FileResult,
|
|
14
|
-
ProgressState,
|
|
15
|
-
SuiteResult,
|
|
16
|
-
TaskResult,
|
|
17
|
-
} from '../types/index.js';
|
|
12
|
+
import type { BenchmarkRun, TaskResult } from '../types/index.js';
|
|
18
13
|
|
|
19
14
|
import { ReporterOutputError } from '../errors/index.js';
|
|
20
15
|
import { BaseReporter } from '../services/reporter-registry.js';
|
|
@@ -40,12 +35,30 @@ interface JsonOutput {
|
|
|
40
35
|
};
|
|
41
36
|
}
|
|
42
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Cache the package version at module load time
|
|
40
|
+
*
|
|
41
|
+
* NOTE: This relies on package.json being at the same relative path from both
|
|
42
|
+
* src/ and dist/ directories (../../package.json). If the build output
|
|
43
|
+
* structure changes, this will break.
|
|
44
|
+
*/
|
|
45
|
+
const cachedPackageVersion = (() => {
|
|
46
|
+
try {
|
|
47
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
48
|
+
const pkgPath = join(__dirname, '..', '..', 'package.json');
|
|
49
|
+
const pkgContent = readFileSync(pkgPath, 'utf8');
|
|
50
|
+
const pkg = JSON.parse(pkgContent) as { version: string };
|
|
51
|
+
return pkg.version;
|
|
52
|
+
} catch {
|
|
53
|
+
// Fallback if package.json cannot be read (shouldn't happen in normal use)
|
|
54
|
+
return 'unknown';
|
|
55
|
+
}
|
|
56
|
+
})();
|
|
57
|
+
|
|
43
58
|
/**
|
|
44
59
|
* JSON reporter for structured output
|
|
45
60
|
*/
|
|
46
61
|
export class JsonReporter extends BaseReporter {
|
|
47
|
-
private currentRun?: BenchmarkRun;
|
|
48
|
-
|
|
49
62
|
private readonly includeMetadata: boolean;
|
|
50
63
|
|
|
51
64
|
private readonly includeStatistics: boolean;
|
|
@@ -54,8 +67,6 @@ export class JsonReporter extends BaseReporter {
|
|
|
54
67
|
|
|
55
68
|
private readonly prettyPrint: boolean;
|
|
56
69
|
|
|
57
|
-
private readonly quiet: boolean;
|
|
58
|
-
|
|
59
70
|
private statistics: {
|
|
60
71
|
fastestTask?: TaskResult;
|
|
61
72
|
slowestTask?: TaskResult;
|
|
@@ -74,8 +85,6 @@ export class JsonReporter extends BaseReporter {
|
|
|
74
85
|
includeStatistics?: boolean;
|
|
75
86
|
outputPath?: string;
|
|
76
87
|
prettyPrint?: boolean;
|
|
77
|
-
quiet?: boolean;
|
|
78
|
-
verbose?: boolean;
|
|
79
88
|
} = {},
|
|
80
89
|
) {
|
|
81
90
|
super('json', options);
|
|
@@ -84,35 +93,6 @@ export class JsonReporter extends BaseReporter {
|
|
|
84
93
|
this.prettyPrint = options.prettyPrint ?? true;
|
|
85
94
|
this.includeStatistics = options.includeStatistics ?? true;
|
|
86
95
|
this.includeMetadata = options.includeMetadata ?? true;
|
|
87
|
-
this.quiet = options.quiet ?? false;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Check if statistics are included
|
|
92
|
-
*/
|
|
93
|
-
areStatisticsIncluded(): boolean {
|
|
94
|
-
return this.includeStatistics;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Get the output path (if configured)
|
|
99
|
-
*/
|
|
100
|
-
getOutputPath(): string | undefined {
|
|
101
|
-
return this.outputPath;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Check if metadata is included
|
|
106
|
-
*/
|
|
107
|
-
isMetadataIncluded(): boolean {
|
|
108
|
-
return this.includeMetadata;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Check if pretty printing is enabled
|
|
113
|
-
*/
|
|
114
|
-
isPrettyPrintEnabled(): boolean {
|
|
115
|
-
return this.prettyPrint;
|
|
116
96
|
}
|
|
117
97
|
|
|
118
98
|
async onEnd(run: BenchmarkRun): Promise<void> {
|
|
@@ -131,41 +111,16 @@ export class JsonReporter extends BaseReporter {
|
|
|
131
111
|
console.error('JSON Reporter Error:', error.message);
|
|
132
112
|
}
|
|
133
113
|
|
|
134
|
-
|
|
135
|
-
// No-op for JSON reporter
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
onFileStart(_file: string): void {
|
|
139
|
-
// No-op for JSON reporter
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
onProgress(_state: ProgressState): void {
|
|
143
|
-
// No-op for JSON reporter - we don't output progress in JSON format
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
onStart(run: BenchmarkRun): void {
|
|
147
|
-
this.currentRun = run;
|
|
114
|
+
onStart(_run: BenchmarkRun): void {
|
|
148
115
|
this.resetStatistics();
|
|
149
116
|
}
|
|
150
117
|
|
|
151
|
-
onSuiteEnd(_result: SuiteResult): void {
|
|
152
|
-
// No-op for JSON reporter
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
onSuiteStart(_suite: string): void {
|
|
156
|
-
// No-op for JSON reporter
|
|
157
|
-
}
|
|
158
|
-
|
|
159
118
|
onTaskResult(result: TaskResult): void {
|
|
160
119
|
if (!result.error) {
|
|
161
120
|
this.updateStatistics(result);
|
|
162
121
|
}
|
|
163
122
|
}
|
|
164
123
|
|
|
165
|
-
onTaskStart(_task: string): void {
|
|
166
|
-
// No-op for JSON reporter
|
|
167
|
-
}
|
|
168
|
-
|
|
169
124
|
/**
|
|
170
125
|
* Build the complete JSON output structure
|
|
171
126
|
*/
|
|
@@ -174,7 +129,7 @@ export class JsonReporter extends BaseReporter {
|
|
|
174
129
|
meta: {
|
|
175
130
|
format: 'modestbench-json',
|
|
176
131
|
timestamp: new Date().toISOString(),
|
|
177
|
-
version:
|
|
132
|
+
version: cachedPackageVersion,
|
|
178
133
|
},
|
|
179
134
|
run: this.includeMetadata ? run : this.sanitizeRun(run),
|
|
180
135
|
};
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Profile Human Reporter
|
|
3
|
+
*
|
|
4
|
+
* Human-readable reporter for profile command. Uses modestbench's synthwave
|
|
5
|
+
* ANSI theme to display profiled functions in an attractive, color-coded
|
|
6
|
+
* format.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
|
|
13
|
+
import type { FilteredProfileData } from '../types/profiler.js';
|
|
14
|
+
|
|
15
|
+
import { ansiChars, colors } from '../utils/ansi.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Reporter options
|
|
19
|
+
*/
|
|
20
|
+
interface ProfileReporterOptions {
|
|
21
|
+
/** Enable color output */
|
|
22
|
+
color?: boolean;
|
|
23
|
+
|
|
24
|
+
/** Group by file */
|
|
25
|
+
groupByFile?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Human-readable profile reporter
|
|
30
|
+
*/
|
|
31
|
+
export class ProfileHumanReporter {
|
|
32
|
+
private readonly groupByFile: boolean;
|
|
33
|
+
|
|
34
|
+
private readonly useColor: boolean;
|
|
35
|
+
|
|
36
|
+
constructor(options: ProfileReporterOptions = {}) {
|
|
37
|
+
this.useColor =
|
|
38
|
+
options.color ??
|
|
39
|
+
(process.stdout.isTTY &&
|
|
40
|
+
process.env.FORCE_COLOR !== '0' &&
|
|
41
|
+
process.env.NO_COLOR == null);
|
|
42
|
+
|
|
43
|
+
this.groupByFile = options.groupByFile ?? false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Generate and print profile report
|
|
48
|
+
*/
|
|
49
|
+
report(data: FilteredProfileData): void {
|
|
50
|
+
this.printHeader(data);
|
|
51
|
+
this.printLine();
|
|
52
|
+
|
|
53
|
+
if (this.groupByFile && data.groupedByFile) {
|
|
54
|
+
this.printGroupedResults(data);
|
|
55
|
+
} else {
|
|
56
|
+
this.printFlatResults(data);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private colorize(color: keyof typeof colors, text: string): string {
|
|
61
|
+
if (!this.useColor) {
|
|
62
|
+
return text;
|
|
63
|
+
}
|
|
64
|
+
return `${colors[color]}${text}${colors.reset}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Format file path - show relative path if within CWD, otherwise absolute
|
|
69
|
+
*/
|
|
70
|
+
private formatPath(filePath: string): string {
|
|
71
|
+
const cwd = process.cwd();
|
|
72
|
+
const absolutePath = path.resolve(filePath);
|
|
73
|
+
|
|
74
|
+
// Check if the file is within the current working directory
|
|
75
|
+
if (absolutePath.startsWith(cwd + path.sep) || absolutePath === cwd) {
|
|
76
|
+
return path.relative(cwd, absolutePath);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return absolutePath;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private getPercentColor(percent: number): keyof typeof colors {
|
|
83
|
+
if (percent >= 10) {
|
|
84
|
+
return 'brightRed';
|
|
85
|
+
}
|
|
86
|
+
if (percent >= 5) {
|
|
87
|
+
return 'brightYellow';
|
|
88
|
+
}
|
|
89
|
+
if (percent >= 2) {
|
|
90
|
+
return 'brightCyan';
|
|
91
|
+
}
|
|
92
|
+
return 'white';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private printFlatResults(data: FilteredProfileData): void {
|
|
96
|
+
const header = `${this.colorize('magenta', ansiChars.block.full.repeat(2))} ${this.colorize('brightWhite', this.colorize('bold', 'Benchmark Candidates'))}`;
|
|
97
|
+
this.printLine(header);
|
|
98
|
+
this.printLine();
|
|
99
|
+
this.printLine('Top functions by execution time:');
|
|
100
|
+
this.printLine();
|
|
101
|
+
|
|
102
|
+
for (const fn of data.functions) {
|
|
103
|
+
// Function name and percentage
|
|
104
|
+
const percentColor = this.getPercentColor(fn.percentage);
|
|
105
|
+
const percent = `${fn.percentage.toFixed(1)}%`;
|
|
106
|
+
const ticks = `(${fn.ticks.toLocaleString()} ticks)`;
|
|
107
|
+
|
|
108
|
+
this.printLine(
|
|
109
|
+
` ${this.colorize('brightWhite', fn.name).padEnd(60)} ${this.colorize(percentColor, percent.padStart(6))} ${this.colorize('dim', ticks)}`,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// File and line
|
|
113
|
+
const displayPath = this.formatPath(fn.file);
|
|
114
|
+
const lineInfo = fn.line ? `:${fn.line}` : '';
|
|
115
|
+
this.printLine(
|
|
116
|
+
` ${this.colorize('brightMagenta', this.colorize('bold', displayPath + lineInfo))}`,
|
|
117
|
+
);
|
|
118
|
+
this.printLine();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
this.printSummary(data);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private printGroupedResults(data: FilteredProfileData): void {
|
|
125
|
+
if (!data.groupedByFile) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const header = `${this.colorize('magenta', ansiChars.block.full.repeat(2))} ${this.colorize('brightWhite', this.colorize('bold', 'Grouped by File'))}`;
|
|
130
|
+
this.printLine(header);
|
|
131
|
+
this.printLine();
|
|
132
|
+
|
|
133
|
+
// Sort files by total percentage
|
|
134
|
+
const sortedFiles = Array.from(data.groupedByFile.entries()).sort(
|
|
135
|
+
(a, b) => {
|
|
136
|
+
const aTotal = a[1].reduce((sum, fn) => sum + fn.percentage, 0);
|
|
137
|
+
const bTotal = b[1].reduce((sum, fn) => sum + fn.percentage, 0);
|
|
138
|
+
return bTotal - aTotal;
|
|
139
|
+
},
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
for (const [file, functions] of sortedFiles) {
|
|
143
|
+
const totalPercent = functions.reduce(
|
|
144
|
+
(sum, fn) => sum + fn.percentage,
|
|
145
|
+
0,
|
|
146
|
+
);
|
|
147
|
+
const totalTicks = functions.reduce((sum, fn) => sum + fn.ticks, 0);
|
|
148
|
+
|
|
149
|
+
const percentColor = this.getPercentColor(totalPercent);
|
|
150
|
+
const percent = `${totalPercent.toFixed(1)}%`;
|
|
151
|
+
const ticks = `(${totalTicks.toLocaleString()} ticks)`;
|
|
152
|
+
|
|
153
|
+
// File header
|
|
154
|
+
const displayPath = this.formatPath(file);
|
|
155
|
+
this.printLine(
|
|
156
|
+
`${this.colorize('magenta', ansiChars.block.dark)} ${this.colorize('brightMagenta', this.colorize('bold', displayPath)).padEnd(60)} ${this.colorize(percentColor, percent.padStart(6))} ${this.colorize('dim', ticks)}`,
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
// Functions in this file
|
|
160
|
+
for (const fn of functions) {
|
|
161
|
+
const fnPercent = `${fn.percentage.toFixed(1)}%`;
|
|
162
|
+
const fnTicks = `(${fn.ticks.toLocaleString()} ticks)`;
|
|
163
|
+
const lineInfo = fn.line ? `:${fn.line}` : '';
|
|
164
|
+
|
|
165
|
+
this.printLine(
|
|
166
|
+
` ${this.colorize('magenta', ansiChars.smallSquare)} ${this.colorize('brightWhite', fn.name).padEnd(58)} ${this.colorize(this.getPercentColor(fn.percentage), fnPercent.padStart(6))} ${this.colorize('dim', fnTicks.padEnd(15))} ${this.colorize('dim', lineInfo)}`,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
this.printLine();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
this.printSummary(data);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private printHeader(data: FilteredProfileData): void {
|
|
177
|
+
const header = `${this.colorize('magenta', ansiChars.block.full.repeat(2))} ${this.colorize('brightWhite', this.colorize('bold', 'Profile Analysis'))}`;
|
|
178
|
+
this.printLine(header);
|
|
179
|
+
this.printLine();
|
|
180
|
+
|
|
181
|
+
if (data.command) {
|
|
182
|
+
this.printLine(`Command: ${this.colorize('cyan', data.command)}`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (data.duration) {
|
|
186
|
+
const durationSec = (data.duration / 1000).toFixed(1);
|
|
187
|
+
this.printLine(`Duration: ${this.colorize('cyan', `${durationSec}s`)}`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
this.printLine(
|
|
191
|
+
`Total Ticks: ${this.colorize('cyan', data.totalTicks.toLocaleString())}`,
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private printLine(text = ''): void {
|
|
196
|
+
console.log(text);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private printSummary(data: FilteredProfileData): void {
|
|
200
|
+
this.printLine(
|
|
201
|
+
`${this.colorize('dim', `... (showing top ${data.totalShown} of ${data.totalFiltered} user functions)`)}`,
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
}
|