modestbench 0.0.1

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.
Files changed (275) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/LICENSE.md +55 -0
  3. package/README.md +699 -0
  4. package/dist/bootstrap.cjs +37 -0
  5. package/dist/bootstrap.cjs.map +1 -0
  6. package/dist/bootstrap.d.cts +17 -0
  7. package/dist/bootstrap.d.cts.map +1 -0
  8. package/dist/bootstrap.d.ts +17 -0
  9. package/dist/bootstrap.d.ts.map +1 -0
  10. package/dist/bootstrap.js +33 -0
  11. package/dist/bootstrap.js.map +1 -0
  12. package/dist/cli/commands/history.cjs +459 -0
  13. package/dist/cli/commands/history.cjs.map +1 -0
  14. package/dist/cli/commands/history.d.cts +34 -0
  15. package/dist/cli/commands/history.d.cts.map +1 -0
  16. package/dist/cli/commands/history.d.ts +34 -0
  17. package/dist/cli/commands/history.d.ts.map +1 -0
  18. package/dist/cli/commands/history.js +422 -0
  19. package/dist/cli/commands/history.js.map +1 -0
  20. package/dist/cli/commands/init.cjs +566 -0
  21. package/dist/cli/commands/init.cjs.map +1 -0
  22. package/dist/cli/commands/init.d.cts +26 -0
  23. package/dist/cli/commands/init.d.cts.map +1 -0
  24. package/dist/cli/commands/init.d.ts +26 -0
  25. package/dist/cli/commands/init.d.ts.map +1 -0
  26. package/dist/cli/commands/init.js +562 -0
  27. package/dist/cli/commands/init.js.map +1 -0
  28. package/dist/cli/commands/run.cjs +285 -0
  29. package/dist/cli/commands/run.cjs.map +1 -0
  30. package/dist/cli/commands/run.d.cts +37 -0
  31. package/dist/cli/commands/run.d.cts.map +1 -0
  32. package/dist/cli/commands/run.d.ts +37 -0
  33. package/dist/cli/commands/run.d.ts.map +1 -0
  34. package/dist/cli/commands/run.js +248 -0
  35. package/dist/cli/commands/run.js.map +1 -0
  36. package/dist/cli/index.cjs +523 -0
  37. package/dist/cli/index.cjs.map +1 -0
  38. package/dist/cli/index.d.cts +58 -0
  39. package/dist/cli/index.d.cts.map +1 -0
  40. package/dist/cli/index.d.ts +58 -0
  41. package/dist/cli/index.d.ts.map +1 -0
  42. package/dist/cli/index.js +515 -0
  43. package/dist/cli/index.js.map +1 -0
  44. package/dist/config/manager.cjs +370 -0
  45. package/dist/config/manager.cjs.map +1 -0
  46. package/dist/config/manager.d.cts +46 -0
  47. package/dist/config/manager.d.cts.map +1 -0
  48. package/dist/config/manager.d.ts +46 -0
  49. package/dist/config/manager.d.ts.map +1 -0
  50. package/dist/config/manager.js +333 -0
  51. package/dist/config/manager.js.map +1 -0
  52. package/dist/config/schema.cjs +182 -0
  53. package/dist/config/schema.cjs.map +1 -0
  54. package/dist/config/schema.d.cts +51 -0
  55. package/dist/config/schema.d.cts.map +1 -0
  56. package/dist/config/schema.d.ts +51 -0
  57. package/dist/config/schema.d.ts.map +1 -0
  58. package/dist/config/schema.js +145 -0
  59. package/dist/config/schema.js.map +1 -0
  60. package/dist/constants.cjs +22 -0
  61. package/dist/constants.cjs.map +1 -0
  62. package/dist/constants.d.cts +10 -0
  63. package/dist/constants.d.cts.map +1 -0
  64. package/dist/constants.d.ts +10 -0
  65. package/dist/constants.d.ts.map +1 -0
  66. package/dist/constants.js +19 -0
  67. package/dist/constants.js.map +1 -0
  68. package/dist/core/benchmark-schema.cjs +135 -0
  69. package/dist/core/benchmark-schema.cjs.map +1 -0
  70. package/dist/core/benchmark-schema.d.cts +139 -0
  71. package/dist/core/benchmark-schema.d.cts.map +1 -0
  72. package/dist/core/benchmark-schema.d.ts +139 -0
  73. package/dist/core/benchmark-schema.d.ts.map +1 -0
  74. package/dist/core/benchmark-schema.js +132 -0
  75. package/dist/core/benchmark-schema.js.map +1 -0
  76. package/dist/core/engine.cjs +669 -0
  77. package/dist/core/engine.cjs.map +1 -0
  78. package/dist/core/engine.d.cts +128 -0
  79. package/dist/core/engine.d.cts.map +1 -0
  80. package/dist/core/engine.d.ts +128 -0
  81. package/dist/core/engine.d.ts.map +1 -0
  82. package/dist/core/engine.js +632 -0
  83. package/dist/core/engine.js.map +1 -0
  84. package/dist/core/engines/accurate-engine.cjs +292 -0
  85. package/dist/core/engines/accurate-engine.cjs.map +1 -0
  86. package/dist/core/engines/accurate-engine.d.cts +63 -0
  87. package/dist/core/engines/accurate-engine.d.cts.map +1 -0
  88. package/dist/core/engines/accurate-engine.d.ts +63 -0
  89. package/dist/core/engines/accurate-engine.d.ts.map +1 -0
  90. package/dist/core/engines/accurate-engine.js +288 -0
  91. package/dist/core/engines/accurate-engine.js.map +1 -0
  92. package/dist/core/engines/index.cjs +21 -0
  93. package/dist/core/engines/index.cjs.map +1 -0
  94. package/dist/core/engines/index.d.cts +16 -0
  95. package/dist/core/engines/index.d.cts.map +1 -0
  96. package/dist/core/engines/index.d.ts +16 -0
  97. package/dist/core/engines/index.d.ts.map +1 -0
  98. package/dist/core/engines/index.js +16 -0
  99. package/dist/core/engines/index.js.map +1 -0
  100. package/dist/core/engines/tinybench-engine.cjs +286 -0
  101. package/dist/core/engines/tinybench-engine.cjs.map +1 -0
  102. package/dist/core/engines/tinybench-engine.d.cts +18 -0
  103. package/dist/core/engines/tinybench-engine.d.cts.map +1 -0
  104. package/dist/core/engines/tinybench-engine.d.ts +18 -0
  105. package/dist/core/engines/tinybench-engine.d.ts.map +1 -0
  106. package/dist/core/engines/tinybench-engine.js +282 -0
  107. package/dist/core/engines/tinybench-engine.js.map +1 -0
  108. package/dist/core/error-manager.cjs +303 -0
  109. package/dist/core/error-manager.cjs.map +1 -0
  110. package/dist/core/error-manager.d.cts +77 -0
  111. package/dist/core/error-manager.d.cts.map +1 -0
  112. package/dist/core/error-manager.d.ts +77 -0
  113. package/dist/core/error-manager.d.ts.map +1 -0
  114. package/dist/core/error-manager.js +299 -0
  115. package/dist/core/error-manager.js.map +1 -0
  116. package/dist/core/loader.cjs +287 -0
  117. package/dist/core/loader.cjs.map +1 -0
  118. package/dist/core/loader.d.cts +55 -0
  119. package/dist/core/loader.d.cts.map +1 -0
  120. package/dist/core/loader.d.ts +55 -0
  121. package/dist/core/loader.d.ts.map +1 -0
  122. package/dist/core/loader.js +250 -0
  123. package/dist/core/loader.js.map +1 -0
  124. package/dist/core/stats-utils.cjs +99 -0
  125. package/dist/core/stats-utils.cjs.map +1 -0
  126. package/dist/core/stats-utils.d.cts +50 -0
  127. package/dist/core/stats-utils.d.cts.map +1 -0
  128. package/dist/core/stats-utils.d.ts +50 -0
  129. package/dist/core/stats-utils.d.ts.map +1 -0
  130. package/dist/core/stats-utils.js +94 -0
  131. package/dist/core/stats-utils.js.map +1 -0
  132. package/dist/index.cjs +64 -0
  133. package/dist/index.cjs.map +1 -0
  134. package/dist/index.d.cts +22 -0
  135. package/dist/index.d.cts.map +1 -0
  136. package/dist/index.d.ts +22 -0
  137. package/dist/index.d.ts.map +1 -0
  138. package/dist/index.js +30 -0
  139. package/dist/index.js.map +1 -0
  140. package/dist/progress/manager.cjs +325 -0
  141. package/dist/progress/manager.cjs.map +1 -0
  142. package/dist/progress/manager.d.cts +125 -0
  143. package/dist/progress/manager.d.cts.map +1 -0
  144. package/dist/progress/manager.d.ts +125 -0
  145. package/dist/progress/manager.d.ts.map +1 -0
  146. package/dist/progress/manager.js +321 -0
  147. package/dist/progress/manager.js.map +1 -0
  148. package/dist/reporters/csv.cjs +250 -0
  149. package/dist/reporters/csv.cjs.map +1 -0
  150. package/dist/reporters/csv.d.cts +92 -0
  151. package/dist/reporters/csv.d.cts.map +1 -0
  152. package/dist/reporters/csv.d.ts +92 -0
  153. package/dist/reporters/csv.d.ts.map +1 -0
  154. package/dist/reporters/csv.js +246 -0
  155. package/dist/reporters/csv.js.map +1 -0
  156. package/dist/reporters/human.cjs +516 -0
  157. package/dist/reporters/human.cjs.map +1 -0
  158. package/dist/reporters/human.d.cts +86 -0
  159. package/dist/reporters/human.d.cts.map +1 -0
  160. package/dist/reporters/human.d.ts +86 -0
  161. package/dist/reporters/human.d.ts.map +1 -0
  162. package/dist/reporters/human.js +509 -0
  163. package/dist/reporters/human.js.map +1 -0
  164. package/dist/reporters/index.cjs +17 -0
  165. package/dist/reporters/index.cjs.map +1 -0
  166. package/dist/reporters/index.d.cts +10 -0
  167. package/dist/reporters/index.d.cts.map +1 -0
  168. package/dist/reporters/index.d.ts +10 -0
  169. package/dist/reporters/index.d.ts.map +1 -0
  170. package/dist/reporters/index.js +10 -0
  171. package/dist/reporters/index.js.map +1 -0
  172. package/dist/reporters/json.cjs +215 -0
  173. package/dist/reporters/json.cjs.map +1 -0
  174. package/dist/reporters/json.d.cts +79 -0
  175. package/dist/reporters/json.d.cts.map +1 -0
  176. package/dist/reporters/json.d.ts +79 -0
  177. package/dist/reporters/json.d.ts.map +1 -0
  178. package/dist/reporters/json.js +211 -0
  179. package/dist/reporters/json.js.map +1 -0
  180. package/dist/reporters/registry.cjs +255 -0
  181. package/dist/reporters/registry.cjs.map +1 -0
  182. package/dist/reporters/registry.d.cts +155 -0
  183. package/dist/reporters/registry.d.cts.map +1 -0
  184. package/dist/reporters/registry.d.ts +155 -0
  185. package/dist/reporters/registry.d.ts.map +1 -0
  186. package/dist/reporters/registry.js +249 -0
  187. package/dist/reporters/registry.js.map +1 -0
  188. package/dist/reporters/simple.cjs +328 -0
  189. package/dist/reporters/simple.cjs.map +1 -0
  190. package/dist/reporters/simple.d.cts +51 -0
  191. package/dist/reporters/simple.d.cts.map +1 -0
  192. package/dist/reporters/simple.d.ts +51 -0
  193. package/dist/reporters/simple.d.ts.map +1 -0
  194. package/dist/reporters/simple.js +321 -0
  195. package/dist/reporters/simple.js.map +1 -0
  196. package/dist/schema/modestbench-config.schema.json +162 -0
  197. package/dist/storage/history.cjs +456 -0
  198. package/dist/storage/history.cjs.map +1 -0
  199. package/dist/storage/history.d.cts +99 -0
  200. package/dist/storage/history.d.cts.map +1 -0
  201. package/dist/storage/history.d.ts +99 -0
  202. package/dist/storage/history.d.ts.map +1 -0
  203. package/dist/storage/history.js +452 -0
  204. package/dist/storage/history.js.map +1 -0
  205. package/dist/types/cli.cjs +21 -0
  206. package/dist/types/cli.cjs.map +1 -0
  207. package/dist/types/cli.d.cts +296 -0
  208. package/dist/types/cli.d.cts.map +1 -0
  209. package/dist/types/cli.d.ts +296 -0
  210. package/dist/types/cli.d.ts.map +1 -0
  211. package/dist/types/cli.js +18 -0
  212. package/dist/types/cli.js.map +1 -0
  213. package/dist/types/core.cjs +14 -0
  214. package/dist/types/core.cjs.map +1 -0
  215. package/dist/types/core.d.cts +380 -0
  216. package/dist/types/core.d.cts.map +1 -0
  217. package/dist/types/core.d.ts +380 -0
  218. package/dist/types/core.d.ts.map +1 -0
  219. package/dist/types/core.js +13 -0
  220. package/dist/types/core.js.map +1 -0
  221. package/dist/types/index.cjs +27 -0
  222. package/dist/types/index.cjs.map +1 -0
  223. package/dist/types/index.d.cts +11 -0
  224. package/dist/types/index.d.cts.map +1 -0
  225. package/dist/types/index.d.ts +11 -0
  226. package/dist/types/index.d.ts.map +1 -0
  227. package/dist/types/index.js +11 -0
  228. package/dist/types/index.js.map +1 -0
  229. package/dist/types/interfaces.cjs +10 -0
  230. package/dist/types/interfaces.cjs.map +1 -0
  231. package/dist/types/interfaces.d.cts +381 -0
  232. package/dist/types/interfaces.d.cts.map +1 -0
  233. package/dist/types/interfaces.d.ts +381 -0
  234. package/dist/types/interfaces.d.ts.map +1 -0
  235. package/dist/types/interfaces.js +9 -0
  236. package/dist/types/interfaces.js.map +1 -0
  237. package/dist/types/utility.cjs +92 -0
  238. package/dist/types/utility.cjs.map +1 -0
  239. package/dist/types/utility.d.cts +330 -0
  240. package/dist/types/utility.d.cts.map +1 -0
  241. package/dist/types/utility.d.ts +330 -0
  242. package/dist/types/utility.d.ts.map +1 -0
  243. package/dist/types/utility.js +78 -0
  244. package/dist/types/utility.js.map +1 -0
  245. package/package.json +211 -0
  246. package/src/bootstrap.ts +35 -0
  247. package/src/cli/commands/history.ts +569 -0
  248. package/src/cli/commands/init.ts +658 -0
  249. package/src/cli/commands/run.ts +346 -0
  250. package/src/cli/index.ts +642 -0
  251. package/src/config/manager.ts +387 -0
  252. package/src/config/schema.ts +188 -0
  253. package/src/constants.ts +21 -0
  254. package/src/core/benchmark-schema.ts +185 -0
  255. package/src/core/engine.ts +888 -0
  256. package/src/core/engines/accurate-engine.ts +408 -0
  257. package/src/core/engines/index.ts +16 -0
  258. package/src/core/engines/tinybench-engine.ts +335 -0
  259. package/src/core/error-manager.ts +372 -0
  260. package/src/core/loader.ts +324 -0
  261. package/src/core/stats-utils.ts +135 -0
  262. package/src/index.ts +46 -0
  263. package/src/progress/manager.ts +415 -0
  264. package/src/reporters/csv.ts +368 -0
  265. package/src/reporters/human.ts +707 -0
  266. package/src/reporters/index.ts +10 -0
  267. package/src/reporters/json.ts +302 -0
  268. package/src/reporters/registry.ts +349 -0
  269. package/src/reporters/simple.ts +459 -0
  270. package/src/storage/history.ts +600 -0
  271. package/src/types/cli.ts +312 -0
  272. package/src/types/core.ts +414 -0
  273. package/src/types/index.ts +18 -0
  274. package/src/types/interfaces.ts +451 -0
  275. package/src/types/utility.ts +446 -0
@@ -0,0 +1,658 @@
1
+ /**
2
+ * ModestBench Init Command
3
+ *
4
+ * Initialize a new benchmark project with configuration files, directory
5
+ * structure, and optional example benchmark files.
6
+ */
7
+
8
+ import {
9
+ access,
10
+ appendFile,
11
+ mkdir,
12
+ readFile,
13
+ writeFile,
14
+ } from 'node:fs/promises';
15
+ import { join, resolve } from 'node:path';
16
+ import { createInterface } from 'node:readline';
17
+
18
+ import type { CliContext } from '../index.js';
19
+
20
+ /**
21
+ * Init command options interface
22
+ */
23
+ interface InitOptions {
24
+ configType: 'js' | 'json' | 'ts' | 'yaml';
25
+ cwd: string;
26
+ examples: boolean;
27
+ force?: boolean | undefined;
28
+ quiet?: boolean | undefined;
29
+ type: 'advanced' | 'basic' | 'library';
30
+ verbose?: boolean | undefined;
31
+ yes?: boolean | undefined;
32
+ }
33
+
34
+ /**
35
+ * Project templates for different initialization types
36
+ */
37
+ const PROJECT_TEMPLATES = {
38
+ advanced: {
39
+ configOptions: {
40
+ iterations: 1000,
41
+ outputDir: './benchmark-results',
42
+ pattern: 'benchmarks/**/*.bench.{js,ts}',
43
+ reporters: ['human', 'json'],
44
+ time: 10000,
45
+ warmup: 50,
46
+ },
47
+ description: 'Feature-rich setup with multiple reporters and configuration',
48
+ directories: ['benchmarks', 'benchmark-results'],
49
+ name: 'Advanced Project',
50
+ },
51
+ basic: {
52
+ configOptions: {
53
+ iterations: 100,
54
+ pattern: 'benchmarks/**/*.bench.{js,ts}',
55
+ reporters: ['human'],
56
+ time: 5000,
57
+ },
58
+ description: 'Simple benchmark setup for small projects',
59
+ directories: ['benchmarks'],
60
+ name: 'Basic Project',
61
+ },
62
+ library: {
63
+ configOptions: {
64
+ bail: false,
65
+ iterations: 5000,
66
+ outputDir: './benchmark-results',
67
+ pattern: 'benchmarks/**/*.bench.{js,ts}',
68
+ reporters: ['human', 'json'],
69
+ time: 15000,
70
+ warmup: 100,
71
+ },
72
+ description: 'Optimized for library performance testing',
73
+ directories: ['benchmarks', 'benchmarks/suites', 'benchmark-results'],
74
+ name: 'Library Project',
75
+ },
76
+ } as const;
77
+
78
+ /**
79
+ * Example benchmark files
80
+ */
81
+ const EXAMPLE_BENCHMARKS = {
82
+ arrayMethods: {
83
+ content: `/**
84
+ * Array Methods Performance Benchmark
85
+ *
86
+ * Compares performance of different array iteration methods.
87
+ */
88
+
89
+ export default {
90
+ name: 'Array Methods',
91
+
92
+ setup() {
93
+ // Setup data for benchmarks
94
+ this.smallArray = Array.from({ length: 100 }, (_, i) => i);
95
+ this.largeArray = Array.from({ length: 10000 }, (_, i) => i);
96
+ },
97
+
98
+ benchmarks: {
99
+ 'for loop (small array)': {
100
+ fn() {
101
+ let sum = 0;
102
+ for (let i = 0; i < this.smallArray.length; i++) {
103
+ sum += this.smallArray[i];
104
+ }
105
+ return sum;
106
+ }
107
+ },
108
+
109
+ 'forEach (small array)': {
110
+ fn() {
111
+ let sum = 0;
112
+ this.smallArray.forEach(n => sum += n);
113
+ return sum;
114
+ }
115
+ },
116
+
117
+ 'reduce (small array)': {
118
+ fn() {
119
+ return this.smallArray.reduce((sum, n) => sum + n, 0);
120
+ }
121
+ },
122
+
123
+ 'for loop (large array)': {
124
+ fn() {
125
+ let sum = 0;
126
+ for (let i = 0; i < this.largeArray.length; i++) {
127
+ sum += this.largeArray[i];
128
+ }
129
+ return sum;
130
+ }
131
+ },
132
+
133
+ 'forEach (large array)': {
134
+ fn() {
135
+ let sum = 0;
136
+ this.largeArray.forEach(n => sum += n);
137
+ return sum;
138
+ }
139
+ },
140
+
141
+ 'reduce (large array)': {
142
+ fn() {
143
+ return this.largeArray.reduce((sum, n) => sum + n, 0);
144
+ }
145
+ }
146
+ }
147
+ };
148
+ `,
149
+ filename: 'array-methods.bench.js',
150
+ },
151
+
152
+ example: {
153
+ content: `/**
154
+ * Example Benchmark File
155
+ *
156
+ * This is a simple example demonstrating basic benchmarking setup.
157
+ */
158
+
159
+ export default {
160
+ name: 'Example Benchmarks',
161
+
162
+ benchmarks: {
163
+ 'simple addition': {
164
+ fn() {
165
+ return 1 + 1;
166
+ }
167
+ },
168
+
169
+ 'array creation': {
170
+ fn() {
171
+ return Array.from({ length: 100 }, (_, i) => i);
172
+ }
173
+ },
174
+
175
+ 'string manipulation': {
176
+ fn() {
177
+ return 'Hello, World!'.toUpperCase();
178
+ }
179
+ }
180
+ }
181
+ };
182
+ `,
183
+ filename: 'example.bench.js',
184
+ },
185
+
186
+ stringOperations: {
187
+ content: `/**
188
+ * String Operations Performance Benchmark
189
+ *
190
+ * Tests various string manipulation techniques.
191
+ */
192
+
193
+ export default {
194
+ name: 'String Operations',
195
+
196
+ setup() {
197
+ this.baseString = 'Hello, World!';
198
+ this.longString = 'Lorem ipsum '.repeat(1000);
199
+ this.template = 'Hello, {name}!';
200
+ },
201
+
202
+ benchmarks: {
203
+ 'string concatenation': {
204
+ fn() {
205
+ return this.baseString + ' How are you?';
206
+ }
207
+ },
208
+
209
+ 'template literals': {
210
+ fn() {
211
+ return \`\${this.baseString} How are you?\`;
212
+ }
213
+ },
214
+
215
+ 'string replace': {
216
+ fn() {
217
+ return this.template.replace('{name}', 'ModestBench');
218
+ }
219
+ },
220
+
221
+ 'string includes': {
222
+ fn() {
223
+ return this.longString.includes('ipsum');
224
+ }
225
+ },
226
+
227
+ 'regex test': {
228
+ fn() {
229
+ return /ipsum/.test(this.longString);
230
+ }
231
+ }
232
+ }
233
+ };
234
+ `,
235
+ filename: 'string-operations.bench.js',
236
+ },
237
+ } as const;
238
+
239
+ /**
240
+ * Prompt user for confirmation with Y/n default to Yes
241
+ */
242
+ const promptUser = async (question: string): Promise<boolean> => {
243
+ const rl = createInterface({
244
+ input: process.stdin,
245
+ output: process.stdout,
246
+ });
247
+
248
+ return new Promise((resolve) => {
249
+ rl.question(question, (answer) => {
250
+ rl.close();
251
+ const normalized = answer.trim().toLowerCase();
252
+ // Default to Yes if empty or starts with 'y'
253
+ resolve(normalized === '' || normalized === 'y' || normalized === 'yes');
254
+ });
255
+ });
256
+ };
257
+
258
+ /**
259
+ * Handle init command
260
+ */
261
+ export const handleInitCommand = async (
262
+ context: CliContext,
263
+ options: InitOptions,
264
+ ): Promise<number> => {
265
+ try {
266
+ const template = PROJECT_TEMPLATES[options.type];
267
+
268
+ if (!options.quiet) {
269
+ console.log(`Initializing ${template.name}...`);
270
+ console.log(template.description);
271
+ console.log();
272
+ }
273
+
274
+ // Check if project already exists
275
+ if (!options.force) {
276
+ const hasConflicts = await checkForConflicts(options);
277
+ if (hasConflicts) {
278
+ console.error('Project files already exist. Use --force to overwrite.');
279
+ return 1; // Already initialized
280
+ }
281
+ }
282
+
283
+ // Create directory structure
284
+ await createDirectories(template.directories, options);
285
+
286
+ // Create configuration file
287
+ await createConfigFile(template.configOptions, options);
288
+
289
+ // Create example benchmarks if requested
290
+ if (options.examples) {
291
+ await createExampleBenchmarks(options);
292
+ }
293
+
294
+ // Create additional files
295
+ await createAdditionalFiles(options);
296
+
297
+ if (!options.quiet) {
298
+ console.log('✅ Project initialized successfully!');
299
+ console.log();
300
+ console.log('Next steps:');
301
+ if (options.examples) {
302
+ console.log(' 1. Run example benchmarks: modestbench run');
303
+ } else {
304
+ console.log(
305
+ ' 1. Create your first benchmark file in the benchmarks/ directory',
306
+ );
307
+ }
308
+ console.log(' 2. Customize configuration in your config file');
309
+ console.log(' 3. Add your own benchmark suites');
310
+ console.log();
311
+ console.log(
312
+ 'Documentation: https://github.com/your-org/modestbench#readme',
313
+ );
314
+ }
315
+
316
+ return 0;
317
+ } catch (error) {
318
+ console.error(
319
+ 'Init command failed:',
320
+ error instanceof Error ? error.message : String(error),
321
+ );
322
+
323
+ if (options.verbose && error instanceof Error && error.stack) {
324
+ console.error('Stack trace:');
325
+ console.error(error.stack);
326
+ }
327
+
328
+ return 5; // Runtime error
329
+ }
330
+ };
331
+
332
+ /**
333
+ * Check for existing files that would conflict
334
+ */
335
+ const checkForConflicts = async (options: InitOptions): Promise<boolean> => {
336
+ const filesToCheck = [
337
+ 'modestbench.config.' + options.configType,
338
+ 'benchmarks',
339
+ ];
340
+
341
+ for (const file of filesToCheck) {
342
+ try {
343
+ await access(resolve(options.cwd, file));
344
+ return true; // File exists, conflict detected
345
+ } catch {
346
+ // File doesn't exist, no conflict
347
+ }
348
+ }
349
+
350
+ return false;
351
+ };
352
+
353
+ /**
354
+ * Handle .gitignore file creation or modification
355
+ */
356
+ const handleGitignore = async (options: InitOptions): Promise<void> => {
357
+ const gitignorePath = resolve(options.cwd, '.gitignore');
358
+ const modestbenchEntry = '.modestbench/';
359
+
360
+ // Default .gitignore content for new files
361
+ const defaultGitignoreContent = `# ModestBench
362
+ benchmark-results/
363
+ .modestbench/
364
+
365
+ # Dependencies
366
+ node_modules/
367
+
368
+ # Environment
369
+ .env
370
+ .env.local
371
+
372
+ # Logs
373
+ *.log
374
+ npm-debug.log*
375
+ yarn-debug.log*
376
+ yarn-error.log*
377
+
378
+ # OS
379
+ .DS_Store
380
+ Thumbs.db
381
+ `;
382
+
383
+ try {
384
+ // Check if .gitignore exists
385
+ let gitignoreExists = false;
386
+ try {
387
+ await access(gitignorePath);
388
+ gitignoreExists = true;
389
+ } catch {
390
+ // File doesn't exist
391
+ }
392
+
393
+ if (!gitignoreExists) {
394
+ // Create new .gitignore with full content
395
+ await writeFile(gitignorePath, defaultGitignoreContent, 'utf8');
396
+ if (options.verbose) {
397
+ console.log(' ✓ .gitignore');
398
+ }
399
+ return;
400
+ }
401
+
402
+ // File exists, check if .modestbench/ is already present
403
+ const existingContent = await readFile(gitignorePath, 'utf8');
404
+
405
+ // Check if .modestbench/ is already in the file
406
+ const hasModestbenchEntry = existingContent
407
+ .split('\n')
408
+ .some((line) => line.trim() === modestbenchEntry);
409
+
410
+ if (hasModestbenchEntry) {
411
+ // Already has the entry, nothing to do
412
+ if (options.verbose) {
413
+ console.log(' ✓ .gitignore (already contains .modestbench/)');
414
+ }
415
+ return;
416
+ }
417
+
418
+ // Determine if we should prompt or auto-add
419
+ let shouldAdd = false;
420
+
421
+ if (options.yes || options.quiet) {
422
+ // Auto-accept in non-interactive mode
423
+ shouldAdd = true;
424
+ } else {
425
+ // Prompt the user
426
+ console.log();
427
+ console.log(
428
+ 'The .modestbench/ directory stores benchmark history and should typically',
429
+ );
430
+ console.log('not be committed to version control.');
431
+ console.log();
432
+
433
+ shouldAdd = await promptUser(
434
+ 'Would you like to add .modestbench/ to .gitignore? (Y/n) ',
435
+ );
436
+ }
437
+
438
+ if (shouldAdd) {
439
+ // Append .modestbench/ to existing .gitignore
440
+ let contentToAppend = '';
441
+
442
+ // Ensure file ends with newline
443
+ if (!existingContent.endsWith('\n')) {
444
+ contentToAppend += '\n';
445
+ }
446
+
447
+ // Add a blank line if the file doesn't end with one
448
+ if (!existingContent.endsWith('\n\n') && existingContent.trim() !== '') {
449
+ contentToAppend += '\n';
450
+ }
451
+
452
+ // Add comment and entry
453
+ contentToAppend += '# ModestBench history\n';
454
+ contentToAppend += modestbenchEntry + '\n';
455
+
456
+ await appendFile(gitignorePath, contentToAppend, 'utf8');
457
+
458
+ if (options.verbose || !options.quiet) {
459
+ console.log(' ✓ Added .modestbench/ to .gitignore');
460
+ }
461
+ } else {
462
+ if (options.verbose) {
463
+ console.log(' ⊘ Skipped adding .modestbench/ to .gitignore');
464
+ }
465
+ }
466
+ } catch (error) {
467
+ // Non-critical, just warn
468
+ console.warn(
469
+ 'Warning: Could not create/modify .gitignore file:',
470
+ error instanceof Error ? error.message : String(error),
471
+ );
472
+ }
473
+ };
474
+
475
+ /**
476
+ * Create additional project files
477
+ */
478
+ const createAdditionalFiles = async (options: InitOptions): Promise<void> => {
479
+ if (!options.quiet) {
480
+ console.log('Creating additional files...');
481
+ }
482
+
483
+ // Handle .gitignore
484
+ await handleGitignore(options);
485
+
486
+ // Create README.md
487
+ const readmeContent = `# Benchmark Project
488
+
489
+ This project uses [ModestBench](https://github.com/your-org/modestbench) for performance testing.
490
+
491
+ ## Getting Started
492
+
493
+ Run all benchmarks:
494
+ \`\`\`bash
495
+ modestbench run
496
+ \`\`\`
497
+
498
+ Run specific benchmarks:
499
+ \`\`\`bash
500
+ modestbench run "benchmarks/array-*.bench.js"
501
+ \`\`\`
502
+
503
+ View benchmark history:
504
+ \`\`\`bash
505
+ modestbench history list
506
+ \`\`\`
507
+
508
+ ## Configuration
509
+
510
+ See \`modestbench.config.*\` for benchmark configuration options.
511
+
512
+ ## Writing Benchmarks
513
+
514
+ Create new benchmark files in the \`benchmarks/\` directory. See the examples for the expected format.
515
+ `;
516
+
517
+ try {
518
+ const readmePath = resolve(options.cwd, 'README.md');
519
+ await writeFile(readmePath, readmeContent, 'utf8');
520
+ if (options.verbose) {
521
+ console.log(' ✓ README.md');
522
+ }
523
+ } catch {
524
+ // Non-critical, just warn
525
+ console.warn('Warning: Could not create README.md file');
526
+ }
527
+ };
528
+
529
+ /**
530
+ * Create configuration file
531
+ */
532
+ const createConfigFile = async (
533
+ configOptions: any,
534
+ options: InitOptions,
535
+ ): Promise<void> => {
536
+ const filename = `modestbench.config.${options.configType}`;
537
+ const filePath = resolve(options.cwd, filename);
538
+
539
+ if (!options.quiet) {
540
+ console.log(`Creating configuration file: ${filename}`);
541
+ }
542
+
543
+ let content: string;
544
+
545
+ switch (options.configType) {
546
+ case 'js':
547
+ content = `export default ${JSON.stringify(configOptions, null, 2)};\n`;
548
+ break;
549
+
550
+ case 'json':
551
+ content = JSON.stringify(configOptions, null, 2);
552
+ break;
553
+
554
+ case 'ts':
555
+ content = `import type { ModestBenchConfig } from 'modestbench';
556
+
557
+ const config: ModestBenchConfig = ${JSON.stringify(configOptions, null, 2)};
558
+
559
+ export default config;
560
+ `;
561
+ break;
562
+
563
+ case 'yaml':
564
+ // Simple YAML generation (could use a proper YAML library)
565
+ content = generateSimpleYaml(configOptions);
566
+ break;
567
+
568
+ default:
569
+ throw new Error(`Unsupported config format: ${options.configType}`);
570
+ }
571
+
572
+ try {
573
+ await writeFile(filePath, content, 'utf8');
574
+ if (options.verbose) {
575
+ console.log(` ✓ ${filename}`);
576
+ }
577
+ } catch (error) {
578
+ throw new Error(
579
+ `Failed to create config file: ${error instanceof Error ? error.message : String(error)}`,
580
+ );
581
+ }
582
+ };
583
+
584
+ /**
585
+ * Create directory structure
586
+ */
587
+ const createDirectories = async (
588
+ directories: readonly string[],
589
+ options: InitOptions,
590
+ ): Promise<void> => {
591
+ if (!options.quiet) {
592
+ console.log('Creating directories...');
593
+ }
594
+
595
+ for (const dir of directories) {
596
+ const dirPath = resolve(options.cwd, dir);
597
+ try {
598
+ await mkdir(dirPath, { recursive: true });
599
+ if (options.verbose) {
600
+ console.log(` ✓ ${dir}/`);
601
+ }
602
+ } catch (error) {
603
+ throw new Error(
604
+ `Failed to create directory ${dir}: ${error instanceof Error ? error.message : String(error)}`,
605
+ );
606
+ }
607
+ }
608
+ };
609
+
610
+ /**
611
+ * Create example benchmark files
612
+ */
613
+ const createExampleBenchmarks = async (options: InitOptions): Promise<void> => {
614
+ if (!options.quiet) {
615
+ console.log('Creating example benchmarks...');
616
+ }
617
+
618
+ const benchmarksDir = resolve(options.cwd, 'benchmarks');
619
+
620
+ for (const [name, example] of Object.entries(EXAMPLE_BENCHMARKS)) {
621
+ const filePath = join(benchmarksDir, example.filename);
622
+
623
+ try {
624
+ await writeFile(filePath, example.content, 'utf8');
625
+ if (options.verbose) {
626
+ console.log(` ✓ ${example.filename}`);
627
+ }
628
+ } catch (error) {
629
+ throw new Error(
630
+ `Failed to create example ${name}: ${error instanceof Error ? error.message : String(error)}`,
631
+ );
632
+ }
633
+ }
634
+ };
635
+
636
+ /**
637
+ * Generate simple YAML from object (basic implementation)
638
+ */
639
+ const generateSimpleYaml = (obj: any, indent = 0): string => {
640
+ const spaces = ' '.repeat(indent);
641
+ let yaml = '';
642
+
643
+ for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {
644
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
645
+ yaml += `${spaces}${key}:\n${generateSimpleYaml(value, indent + 2)}`;
646
+ } else if (Array.isArray(value)) {
647
+ yaml += `${spaces}${key}:\n`;
648
+ for (const item of value) {
649
+ yaml += `${spaces} - ${item}\n`;
650
+ }
651
+ } else {
652
+ const formattedValue = typeof value === 'string' ? `"${value}"` : value;
653
+ yaml += `${spaces}${key}: ${formattedValue}\n`;
654
+ }
655
+ }
656
+
657
+ return yaml;
658
+ };