@monorepolint/utils 0.6.0-alpha.1 → 0.6.0-alpha.3

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 (67) hide show
  1. package/.turbo/turbo-clean.log +1 -1
  2. package/.turbo/turbo-compile-typescript.log +1 -1
  3. package/.turbo/turbo-lint.log +7 -7
  4. package/.turbo/turbo-test.log +36 -23
  5. package/.turbo/turbo-transpile-typescript.log +5 -5
  6. package/CHANGELOG.md +28 -0
  7. package/build/js/index.js +502 -461
  8. package/build/js/index.js.map +1 -1
  9. package/build/tsconfig.tsbuildinfo +1 -1
  10. package/build/types/CachingHost.d.ts +0 -2
  11. package/build/types/CachingHost.d.ts.map +1 -1
  12. package/build/types/Host.d.ts +0 -1
  13. package/build/types/Host.d.ts.map +1 -1
  14. package/build/types/PackageJson.d.ts.map +1 -1
  15. package/build/types/SimpleHost.d.ts +0 -2
  16. package/build/types/SimpleHost.d.ts.map +1 -1
  17. package/build/types/Table.d.ts.map +1 -1
  18. package/build/types/findWorkspaceDir.d.ts.map +1 -1
  19. package/build/types/getPackageNameToDir.d.ts.map +1 -1
  20. package/build/types/getWorkspacePackageDirs.d.ts.map +1 -1
  21. package/build/types/index.d.ts +7 -7
  22. package/build/types/index.d.ts.map +1 -1
  23. package/build/types/matchesAnyGlob.d.ts.map +1 -1
  24. package/build/types/mutateJson.d.ts.map +1 -1
  25. package/build/types/nanosecondsToSanity.d.ts.map +1 -1
  26. package/coverage/AggregateTiming.ts.html +295 -0
  27. package/coverage/CachingHost.ts.html +1786 -0
  28. package/coverage/Host.ts.html +199 -0
  29. package/coverage/PackageJson.ts.html +151 -0
  30. package/coverage/SimpleHost.ts.html +286 -0
  31. package/coverage/Table.ts.html +1156 -0
  32. package/coverage/Timing.ts.html +247 -0
  33. package/coverage/base.css +224 -0
  34. package/coverage/block-navigation.js +87 -0
  35. package/coverage/clover.xml +1272 -0
  36. package/coverage/coverage-final.json +15 -0
  37. package/coverage/favicon.png +0 -0
  38. package/coverage/findWorkspaceDir.ts.html +223 -0
  39. package/coverage/getPackageNameToDir.ts.html +199 -0
  40. package/coverage/getWorkspacePackageDirs.ts.html +331 -0
  41. package/coverage/index.html +311 -0
  42. package/coverage/index.ts.html +145 -0
  43. package/coverage/matchesAnyGlob.ts.html +550 -0
  44. package/coverage/mutateJson.ts.html +136 -0
  45. package/coverage/nanosecondsToSanity.ts.html +121 -0
  46. package/coverage/prettify.css +1 -0
  47. package/coverage/prettify.js +2 -0
  48. package/coverage/sort-arrow-sprite.png +0 -0
  49. package/coverage/sorter.js +196 -0
  50. package/package.json +12 -17
  51. package/src/AggregateTiming.ts +1 -1
  52. package/src/CachingHost.ts +110 -34
  53. package/src/Host.ts +5 -1
  54. package/src/PackageJson.ts +3 -3
  55. package/src/SimpleHost.ts +14 -3
  56. package/src/Table.ts +62 -23
  57. package/src/__tests__/CachingHost.spec.ts +203 -166
  58. package/src/findWorkspaceDir.ts +3 -3
  59. package/src/getPackageNameToDir.ts +6 -2
  60. package/src/getWorkspacePackageDirs.ts +22 -10
  61. package/src/index.ts +7 -7
  62. package/src/matchesAnyGlob.ts +12 -3
  63. package/src/mutateJson.ts +5 -1
  64. package/src/nanosecondsToSanity.ts +3 -1
  65. package/vitest.config.mjs +17 -0
  66. package/vitest.config.mjs.timestamp-1736878329730-aa478e2241542.mjs +18 -0
  67. package/jest.config.cjs +0 -4
package/build/js/index.js CHANGED
@@ -1,149 +1,291 @@
1
- // src/getWorkspacePackageDirs.ts
2
- import { existsSync } from "fs";
3
- import * as glob from "glob";
4
- import * as path from "node:path";
5
- import * as fs from "node:fs";
6
- import * as readYamlFile from "read-yaml-file";
7
- import { findPackages } from "find-packages";
8
- async function findPNPMWorkspacePackages(workspaceRoot) {
9
- workspaceRoot = fs.realpathSync(workspaceRoot);
10
- const workspaceManifest = await readYamlFile.default(
11
- path.join(workspaceRoot, "pnpm-workspace.yaml")
12
- );
13
- return findPackages(workspaceRoot, {
14
- ignore: ["**/node_modules/**", "**/bower_components/**"],
15
- includeRoot: true,
16
- patterns: workspaceManifest.packages
17
- });
1
+ // src/nanosecondsToSanity.ts
2
+ function nanosecondsToSanity(n, precision = 9) {
3
+ return n / BigInt(1e9) + "." + ("" + n % BigInt(1e9)).padStart(9, "0").substring(0, precision) + "s";
18
4
  }
19
- async function getWorkspacePackageDirs(host, workspaceDir, resolvePaths = false) {
20
- const packageJson = host.readJson(path.join(workspaceDir, "package.json"));
21
- const isPnpmWorkspace = host.exists(path.join(workspaceDir, "pnpm-workspace.yaml"));
22
- if (isPnpmWorkspace) {
23
- const workspacePackages = await findPNPMWorkspacePackages(workspaceDir);
24
- if (workspacePackages.length === 0) {
25
- throw new Error("Invalid workspaceDir: " + workspaceDir);
5
+
6
+ // src/Table.ts
7
+ var Table = class {
8
+ #rows = [];
9
+ #config;
10
+ #columnWidths = [];
11
+ #footer = [];
12
+ #footerRowConfig;
13
+ #totalWidth = 0;
14
+ constructor(config) {
15
+ this.#config = {
16
+ padding: 2,
17
+ ...config
18
+ };
19
+ this.#columnWidths.fill(0, 0, config.columns.length);
20
+ if (config.showFooter) {
21
+ this.#footerRowConfig = [];
22
+ for (const columnConfig of config.columns) {
23
+ if (columnConfig.footer === void 0) {
24
+ throw new Error("Must specify footer fields when showFooter is true");
25
+ } else if (typeof columnConfig.footer === "string") {
26
+ this.#footerRowConfig.push({
27
+ type: "string",
28
+ alignment: "left",
29
+ aggregate: "static",
30
+ value: columnConfig.footer
31
+ });
32
+ } else if ("value" in columnConfig.footer) {
33
+ this.#footerRowConfig.push({
34
+ type: "string",
35
+ alignment: "left",
36
+ ...columnConfig.footer
37
+ });
38
+ } else if ("aggregate" in columnConfig.footer) {
39
+ if (columnConfig.type !== "bigint") {
40
+ throw new Error("expecting bigint for aggregate");
41
+ }
42
+ this.#footerRowConfig.push({
43
+ type: columnConfig.type,
44
+ renderAs: columnConfig.renderAs,
45
+ precision: columnConfig.precision,
46
+ alignment: "right",
47
+ ...columnConfig.footer
48
+ });
49
+ }
50
+ }
26
51
  }
27
- return workspacePackages.map((project) => project.dir).filter((d) => d !== workspaceDir);
28
52
  }
29
- if (!packageJson.workspaces) {
30
- throw new Error("Unsupported! Monorepo is not backed by either pnpm nor yarn workspaces.");
53
+ addRow(...data) {
54
+ this.#rows.push(data);
31
55
  }
32
- const ret = [];
33
- const packageGlobs = Array.isArray(packageJson.workspaces) ? packageJson.workspaces : packageJson.workspaces.packages || [];
34
- for (const pattern of packageGlobs) {
35
- for (const packagePath of glob.sync(pattern, { cwd: workspaceDir })) {
36
- const packageJsonPath = path.join(workspaceDir, packagePath, "package.json");
37
- if (existsSync(packageJsonPath)) {
38
- if (resolvePaths === true) {
39
- ret.push(path.resolve(path.join(workspaceDir, packagePath)));
40
- } else {
41
- ret.push(packagePath);
56
+ #sumColumn(c) {
57
+ let total = BigInt(0);
58
+ for (const row of this.#rows) {
59
+ total += row[c];
60
+ }
61
+ return total;
62
+ }
63
+ #updateFooterRow() {
64
+ const footerRowConfig = this.#footerRowConfig;
65
+ if (footerRowConfig) {
66
+ for (let c = 0; c < footerRowConfig.length; c++) {
67
+ const footerColConfig = footerRowConfig[c];
68
+ switch (footerColConfig.aggregate) {
69
+ case "sum":
70
+ this.#footer[c] = this.#sumColumn(c);
71
+ break;
72
+ case "average":
73
+ this.#footer[c] = this.#sumColumn(c) / BigInt(this.#rows.length);
74
+ break;
75
+ case "static":
76
+ this.#footer[c] = footerColConfig.value;
77
+ break;
42
78
  }
43
79
  }
44
80
  }
45
81
  }
46
- return ret;
47
- }
48
-
49
- // src/mutateJson.ts
50
- function mutateJson(path4, host, mutator) {
51
- let file = host.readJson(path4);
52
- file = mutator(file);
53
- host.writeJson(path4, file);
54
- }
55
-
56
- // src/findWorkspaceDir.ts
57
- import * as path2 from "path";
58
- import * as fs2 from "fs";
59
- import { findUp } from "find-up";
60
- async function findPnpmWorkspaceDir(cwd) {
61
- const workspaceManifestLocation = await findUp("pnpm-workspace.yaml", {
62
- cwd: await fs2.promises.realpath(cwd)
63
- });
64
- return workspaceManifestLocation && path2.dirname(workspaceManifestLocation);
65
- }
66
- async function findWorkspaceDir(host, dir) {
67
- const maybePnpmWorkspaceDir = await findPnpmWorkspaceDir(dir);
68
- if (maybePnpmWorkspaceDir != null) {
69
- return maybePnpmWorkspaceDir;
82
+ #calculateColumnWidths() {
83
+ var _a;
84
+ this.#columnWidths.fill(0, 0, this.#config.columns.length);
85
+ for (let c = 0; c < this.#config.columns.length; c++) {
86
+ const colConfig = this.#config.columns[c];
87
+ this.#columnWidths[c] = Math.max(
88
+ (this.#config.columns[c].header ?? "").length,
89
+ ...this.#rows.map(
90
+ (a) => this.#getCellValueAsString(a[c], colConfig).length
91
+ ),
92
+ this.#footer && this.#footerRowConfig ? this.#getCellValueAsString(
93
+ ((_a = this.#footer) == null ? void 0 : _a[c]) ?? "",
94
+ this.#footerRowConfig[c]
95
+ ).length : 0
96
+ );
97
+ }
98
+ this.#totalWidth = 0;
99
+ for (const colWidth of this.#columnWidths) {
100
+ this.#totalWidth += colWidth;
101
+ }
102
+ this.#totalWidth += (this.#columnWidths.length - 1) * this.#config.padding;
70
103
  }
71
- const packagePath = path2.join(dir, "package.json");
72
- if (host.exists(packagePath)) {
73
- const packageJson = host.readJson(packagePath);
74
- if (packageJson.workspaces !== void 0) {
75
- return dir;
104
+ #printSeparator(fillString) {
105
+ const paddingString = "".padStart(this.#config.padding, " ");
106
+ let hr2 = "";
107
+ for (let c = 0; c < this.#columnWidths.length; c++) {
108
+ hr2 += "".padStart(this.#columnWidths[c], fillString) + paddingString;
76
109
  }
110
+ hr2 = hr2.trimRight();
111
+ console.log(hr2);
77
112
  }
78
- const nextDir = path2.normalize(path2.join(dir, ".."));
79
- if (nextDir === dir) {
80
- return void 0;
113
+ #printHeaderRow() {
114
+ if (this.#config.showHeader) {
115
+ const colConfigs = this.#config.columns;
116
+ const paddingString = "".padStart(this.#config.padding, " ");
117
+ let hr = "";
118
+ for (let c = 0; c < colConfigs.length; c++) {
119
+ const heading = colConfigs[c].header ?? "";
120
+ hr += heading.padEnd(this.#columnWidths[c], " ") + paddingString;
121
+ }
122
+ hr = hr.trimRight();
123
+ console.log(hr);
124
+ this.#printSeparator("-");
125
+ }
81
126
  }
82
- return findWorkspaceDir(host, nextDir);
83
- }
84
-
85
- // src/getPackageNameToDir.ts
86
- import { join as pathJoin } from "path";
87
- async function getPackageNameToDir(host, workspaceDir, resolvePaths = false) {
88
- const ret = /* @__PURE__ */ new Map();
89
- const workspacePackages = await getWorkspacePackageDirs(host, workspaceDir, resolvePaths);
90
- for (const packageDir of workspacePackages) {
91
- const packagePath = pathJoin(packageDir, "package.json");
92
- const { name } = host.readJson(packagePath);
93
- if (name === void 0) {
94
- throw new Error(`Package needs a name: ${packagePath}`);
127
+ #printFooterRow() {
128
+ const footerRow = this.#footer;
129
+ if (footerRow) {
130
+ this.#printSeparator("=");
131
+ const paddingString = "".padStart(this.#config.padding, " ");
132
+ let hr = "";
133
+ for (let c = 0; c < footerRow.length; c++) {
134
+ hr += this.#getCellValueAligned(footerRow[c], this.#footerRowConfig[c], c) + paddingString;
135
+ }
136
+ hr = hr.trimRight();
137
+ console.log(hr);
95
138
  }
96
- ret.set(name, packageDir);
97
139
  }
98
- return ret;
99
- }
100
-
101
- // src/SimpleHost.ts
102
- import * as realFs from "fs";
103
- var SimpleHost = class {
104
- constructor(fs3 = realFs) {
105
- this.fs = fs3;
140
+ print() {
141
+ if (this.#config.sortColumn !== void 0) {
142
+ }
143
+ this.#updateFooterRow();
144
+ this.#calculateColumnWidths();
145
+ console.log();
146
+ console.log(`${this.#config.title}`);
147
+ console.log("".padStart(this.#totalWidth, "="));
148
+ const paddingString = "".padStart(this.#config.padding, " ");
149
+ if (this.#config.showHeader) {
150
+ this.#printHeaderRow();
151
+ }
152
+ for (let r = 0; r < this.#rows.length; r++) {
153
+ let rowText = "";
154
+ for (let c = 0; c < this.#config.columns.length; c++) {
155
+ rowText += this.getEntryAsStringAligned(c, r) + paddingString;
156
+ }
157
+ rowText.trim();
158
+ console.log(rowText);
159
+ }
160
+ if (this.#config.showFooter) this.#printFooterRow();
161
+ console.log();
106
162
  }
107
- mkdir(directoryPath, opts) {
108
- this.fs.mkdirSync(directoryPath, { recursive: (opts == null ? void 0 : opts.recursive) ?? false });
163
+ #getCellValueAsString(value, config) {
164
+ if (config.type === "bigint" && config.renderAs === "nanoseconds") {
165
+ return nanosecondsToSanity(value, config.precision ?? 9);
166
+ } else {
167
+ return "" + value;
168
+ }
109
169
  }
110
- rmdir(directoryPath) {
111
- this.fs.rmdirSync(directoryPath);
170
+ #getCellValueAligned(value, config, column) {
171
+ let result;
172
+ if (config.type === "bigint" && config.renderAs === "nanoseconds") {
173
+ result = nanosecondsToSanity(value, config.precision ?? 9);
174
+ } else {
175
+ result = "" + value;
176
+ }
177
+ if (config.alignment === "left") {
178
+ return result.padEnd(this.#columnWidths[column]);
179
+ } else {
180
+ return result.padStart(this.#columnWidths[column]);
181
+ }
112
182
  }
113
- exists(path4) {
114
- return this.fs.existsSync(path4);
183
+ getEntryAsString(colNum, rowNum) {
184
+ const config = this.#config.columns[colNum];
185
+ if (config.type === "bigint" && config.renderAs === "nanoseconds") {
186
+ return nanosecondsToSanity(
187
+ this.#rows[rowNum][colNum],
188
+ config.precision ?? 9
189
+ );
190
+ } else {
191
+ return "" + this.#rows[rowNum][colNum];
192
+ }
115
193
  }
116
- writeFile(path4, body, opts) {
117
- if (opts) {
118
- this.fs.writeFileSync(path4, body, { encoding: opts.encoding });
194
+ getEntryAsStringAligned(colNum, rowNum) {
195
+ const config = this.#config.columns[colNum];
196
+ let result;
197
+ if (config.type === "bigint" && config.renderAs === "nanoseconds") {
198
+ result = nanosecondsToSanity(
199
+ this.#rows[rowNum][colNum],
200
+ config.precision ?? 9
201
+ );
119
202
  } else {
120
- this.fs.writeFileSync(path4, body);
203
+ result = "" + this.#rows[rowNum][colNum];
204
+ }
205
+ if (config.alignment === "left") {
206
+ return result.padEnd(this.#columnWidths[colNum]);
207
+ } else {
208
+ return result.padStart(this.#columnWidths[colNum]);
121
209
  }
122
210
  }
123
- readFile(path4, opts) {
124
- if (opts == null ? void 0 : opts.asJson) {
125
- return JSON.parse(this.fs.readFileSync(path4, "utf-8"));
211
+ getColumnWidth(colNum, config) {
212
+ let maxWidth = Math.max(
213
+ (config.header ?? "").length,
214
+ this.#footer && this.#footerRowConfig ? this.#getCellValueAsString(
215
+ this.#footer[colNum],
216
+ this.#footerRowConfig[colNum]
217
+ ).length : 0
218
+ );
219
+ for (let r = 0; r < this.#rows.length; r++) {
220
+ maxWidth = Math.max(maxWidth, this.getEntryAsString(colNum, r).length);
126
221
  }
127
- return this.fs.readFileSync(path4, opts == null ? void 0 : opts.encoding);
222
+ return maxWidth;
128
223
  }
129
- deleteFile(path4) {
130
- this.fs.unlinkSync(path4);
224
+ };
225
+
226
+ // src/AggregateTiming.ts
227
+ var AggregateTiming = class {
228
+ constructor(title) {
229
+ this.title = title;
131
230
  }
132
- readJson(filename) {
133
- const contents = this.fs.readFileSync(filename, "utf-8");
134
- return JSON.parse(contents);
231
+ #data = /* @__PURE__ */ new Map();
232
+ #last;
233
+ start(name) {
234
+ const time = process.hrtime.bigint();
235
+ if (this.#last) {
236
+ this.#last.total += time;
237
+ }
238
+ let data = this.#data.get(name);
239
+ if (data === void 0) {
240
+ data = { count: 1, total: -time };
241
+ this.#data.set(name, data);
242
+ } else {
243
+ data.total -= time;
244
+ data.count++;
245
+ }
246
+ this.#last = data;
135
247
  }
136
- writeJson(path4, o) {
137
- return this.fs.writeFileSync(path4, JSON.stringify(o, void 0, 2) + "\n");
248
+ stop() {
249
+ const time = process.hrtime.bigint();
250
+ if (this.#last) {
251
+ this.#last.total += time;
252
+ this.#last = void 0;
253
+ }
138
254
  }
139
- flush() {
140
- return Promise.resolve();
255
+ printResults() {
256
+ const table = new Table({
257
+ sortColumn: -1,
258
+ showFooter: true,
259
+ showHeader: true,
260
+ title: this.title,
261
+ columns: [
262
+ {
263
+ header: "Duration",
264
+ type: "bigint",
265
+ renderAs: "nanoseconds",
266
+ footer: { aggregate: "sum" }
267
+ },
268
+ { header: "Task", type: "string", alignment: "left", footer: "TOTAL" },
269
+ { header: "Count", type: "bigint", footer: { aggregate: "sum" } },
270
+ { header: "Avg", type: "bigint", footer: { aggregate: "average" } }
271
+ ]
272
+ });
273
+ for (const [name, value] of this.#data) {
274
+ table.addRow(
275
+ value.total,
276
+ name,
277
+ BigInt(value.count),
278
+ // fixme this can be a number later
279
+ value.total / BigInt(value.count)
280
+ );
281
+ }
282
+ table.print();
141
283
  }
142
284
  };
143
285
 
144
286
  // src/CachingHost.ts
145
- import * as realFs2 from "node:fs";
146
- import * as path3 from "node:path";
287
+ import * as realFs from "node:fs";
288
+ import * as path from "node:path";
147
289
  function assertNoTombstone(node) {
148
290
  if (node.tombstone) {
149
291
  throw new Error(`Unexpected tombstone ${JSON.stringify(node)}`);
@@ -170,33 +312,34 @@ function assertHasParent(node) {
170
312
  }
171
313
  }
172
314
  var CachingHost = class {
173
- constructor(fs3 = realFs2) {
315
+ constructor(fs3 = realFs) {
174
316
  this.fs = fs3;
175
317
  }
176
318
  // We need many trees because of windows, key is the `root`
177
319
  #trees = /* @__PURE__ */ new Map();
178
320
  #replaceNode(node, partialNewNode) {
179
- if (!node.parent)
180
- throw new Error("Cannot replace root node");
321
+ if (!node.parent) throw new Error("Cannot replace root node");
181
322
  const newNode = {
182
323
  ...partialNewNode,
183
324
  fullPath: node.fullPath,
184
325
  parent: node.parent,
185
326
  dir: node.dir
186
327
  };
187
- node.parent.dir.set(path3.basename(node.fullPath), newNode);
328
+ node.parent.dir.set(path.basename(node.fullPath), newNode);
188
329
  return newNode;
189
330
  }
190
331
  #unstubDirectory(node) {
191
332
  for (const child of this.fs.readdirSync(node.fullPath)) {
192
- this.#getNode(path3.join(node.fullPath, child));
333
+ this.#getNode(path.join(node.fullPath, child));
193
334
  }
194
335
  node.stub = false;
195
336
  }
196
337
  #stubify(filePath, parent) {
197
- const canonicalPath = path3.resolve(filePath);
198
- if (!parent && canonicalPath !== path3.parse(canonicalPath).root) {
199
- throw new Error(`parent can only be null if path is root. Instead got: ${canonicalPath}`);
338
+ const canonicalPath = path.resolve(filePath);
339
+ if (!parent && canonicalPath !== path.parse(canonicalPath).root) {
340
+ throw new Error(
341
+ `parent can only be null if path is root. Instead got: ${canonicalPath}`
342
+ );
200
343
  }
201
344
  const stat = this.fs.lstatSync(canonicalPath);
202
345
  let node;
@@ -226,15 +369,19 @@ var CachingHost = class {
226
369
  needsFlush: false
227
370
  };
228
371
  } else {
229
- throw new Error(`what is not a file nor symlink nor directory? nothing we care about: ${canonicalPath}`);
372
+ throw new Error(
373
+ `what is not a file nor symlink nor directory? nothing we care about: ${canonicalPath}`
374
+ );
230
375
  }
231
376
  if (!parent && node.type === "dir") {
232
377
  this.#trees.set(canonicalPath, node);
233
378
  return node;
234
379
  } else if (parent) {
235
- parent.dir.set(path3.basename(canonicalPath), node);
380
+ parent.dir.set(path.basename(canonicalPath), node);
236
381
  } else {
237
- throw new Error(`root can only be a dir, got ${JSON.stringify(node)} for path: ${canonicalPath}`);
382
+ throw new Error(
383
+ `root can only be a dir, got ${JSON.stringify(node)} for path: ${canonicalPath}`
384
+ );
238
385
  }
239
386
  return node;
240
387
  }
@@ -243,13 +390,13 @@ var CachingHost = class {
243
390
  * You should check the `fullPath` of the result.
244
391
  */
245
392
  #getNearestAncestorNode(filePath) {
246
- const canonicalPath = path3.resolve(filePath);
247
- const { root } = path3.parse(canonicalPath);
393
+ const canonicalPath = path.resolve(filePath);
394
+ const { root } = path.parse(canonicalPath);
248
395
  const parts = [];
249
396
  let maybePath = canonicalPath;
250
397
  while (maybePath !== root) {
251
- parts.unshift(path3.basename(maybePath));
252
- maybePath = path3.dirname(maybePath);
398
+ parts.unshift(path.basename(maybePath));
399
+ maybePath = path.dirname(maybePath);
253
400
  }
254
401
  let curPath = root;
255
402
  let curNode = this.#trees.get(root) ?? this.#stubify(curPath, void 0);
@@ -258,7 +405,9 @@ var CachingHost = class {
258
405
  assertNoTombstone(curNode);
259
406
  assertNotType(curNode, "file");
260
407
  if (curNode.type === "symlink") {
261
- const linkedNode = this.#getNodeResolvingSymlinks(path3.resolve(path3.dirname(curPath), curNode.symlink));
408
+ const linkedNode = this.#getNodeResolvingSymlinks(
409
+ path.resolve(path.dirname(curPath), curNode.symlink)
410
+ );
262
411
  assertExists(linkedNode);
263
412
  assertNoTombstone(linkedNode);
264
413
  assertType(linkedNode, "dir");
@@ -266,16 +415,18 @@ var CachingHost = class {
266
415
  }
267
416
  assertType(curNode, "dir");
268
417
  assertNoTombstone(curNode);
269
- curNode = curNode.dir.get(part) ?? this.#stubify(path3.join(curNode.fullPath, part), curNode);
270
- curPath = path3.join(curPath, part);
418
+ curNode = curNode.dir.get(part) ?? this.#stubify(path.join(curNode.fullPath, part), curNode);
419
+ curPath = path.join(curPath, part);
271
420
  }
272
421
  } catch (e) {
273
422
  }
274
423
  return { pathWithSymlinks: curPath, node: curNode };
275
424
  }
276
425
  #getNode(filePath) {
277
- const canonicalPath = path3.resolve(filePath);
278
- const { pathWithSymlinks, node } = this.#getNearestAncestorNode(canonicalPath);
426
+ const canonicalPath = path.resolve(filePath);
427
+ const { pathWithSymlinks, node } = this.#getNearestAncestorNode(
428
+ canonicalPath
429
+ );
279
430
  if (pathWithSymlinks === canonicalPath) {
280
431
  return node;
281
432
  }
@@ -283,24 +434,23 @@ var CachingHost = class {
283
434
  }
284
435
  #getNodeResolvingSymlinks(filePath, follows = 100) {
285
436
  const node = this.#getNode(filePath);
286
- if (!node || node.type !== "symlink")
287
- return node;
288
- if (follows === 0)
289
- throw new Error("Exhausted symlink follows");
437
+ if (!node || node.type !== "symlink") return node;
438
+ if (follows === 0) throw new Error("Exhausted symlink follows");
290
439
  return this.#getNodeResolvingSymlinks(node.symlink, follows--);
291
440
  }
292
441
  mkdir(filePath, opts = { recursive: false }) {
293
- const canonicalPath = path3.resolve(filePath);
294
- const { node, pathWithSymlinks } = this.#getNearestAncestorNode(canonicalPath);
442
+ const canonicalPath = path.resolve(filePath);
443
+ const { node, pathWithSymlinks } = this.#getNearestAncestorNode(
444
+ canonicalPath
445
+ );
295
446
  if (filePath === pathWithSymlinks) {
296
447
  assertType(node, "dir");
297
448
  assertHasParent(node);
298
- if (!node.tombstone)
299
- return;
300
- } else if (path3.dirname(filePath) === pathWithSymlinks) {
449
+ if (!node.tombstone) return;
450
+ } else if (path.dirname(filePath) === pathWithSymlinks) {
301
451
  assertType(node, "dir");
302
452
  assertNoTombstone(node);
303
- node.dir.set(path3.basename(filePath), {
453
+ node.dir.set(path.basename(filePath), {
304
454
  type: "dir",
305
455
  fullPath: filePath,
306
456
  parent: node,
@@ -309,15 +459,17 @@ var CachingHost = class {
309
459
  });
310
460
  return;
311
461
  }
312
- if (!opts.recursive && path3.dirname(canonicalPath) !== pathWithSymlinks) {
462
+ if (!opts.recursive && path.dirname(canonicalPath) !== pathWithSymlinks) {
313
463
  throw new Error("no such file or directory");
314
464
  }
315
465
  const rootPath = pathWithSymlinks;
316
466
  let maybePath = canonicalPath;
317
467
  const toMake = [];
318
468
  while (maybePath !== rootPath) {
319
- toMake.unshift(path3.resolve(node.fullPath, path3.relative(rootPath, maybePath)));
320
- maybePath = path3.dirname(maybePath);
469
+ toMake.unshift(
470
+ path.resolve(node.fullPath, path.relative(rootPath, maybePath))
471
+ );
472
+ maybePath = path.dirname(maybePath);
321
473
  }
322
474
  for (const dirToMake of toMake) {
323
475
  this.mkdir(dirToMake);
@@ -370,7 +522,7 @@ var CachingHost = class {
370
522
  }
371
523
  writeFile(filePath, body, opts) {
372
524
  const fileContentsAsBuffer = typeof body === "string" ? Buffer.from(body, opts == null ? void 0 : opts.encoding) : Buffer.from(body);
373
- const canonicalPath = path3.resolve(filePath);
525
+ const canonicalPath = path.resolve(filePath);
374
526
  const existingNode = this.#getNodeResolvingSymlinks(canonicalPath);
375
527
  if (existingNode) {
376
528
  if (existingNode.type === "dir") {
@@ -383,11 +535,13 @@ var CachingHost = class {
383
535
  });
384
536
  return;
385
537
  }
386
- const maybeDirNode = this.#getNodeResolvingSymlinks(path3.dirname(canonicalPath));
538
+ const maybeDirNode = this.#getNodeResolvingSymlinks(
539
+ path.dirname(canonicalPath)
540
+ );
387
541
  assertExists(maybeDirNode);
388
542
  assertType(maybeDirNode, "dir");
389
543
  assertNoTombstone(maybeDirNode);
390
- maybeDirNode.dir.set(path3.basename(canonicalPath), {
544
+ maybeDirNode.dir.set(path.basename(canonicalPath), {
391
545
  type: "file",
392
546
  fullPath: canonicalPath,
393
547
  parent: maybeDirNode,
@@ -396,10 +550,9 @@ var CachingHost = class {
396
550
  });
397
551
  }
398
552
  deleteFile(filePath) {
399
- const canonicalPath = path3.resolve(filePath);
553
+ const canonicalPath = path.resolve(filePath);
400
554
  const node = this.#getNode(canonicalPath);
401
- if (!node || node.type === "file" && node.tombstone === true)
402
- return;
555
+ if (!node || node.type === "file" && node.tombstone === true) return;
403
556
  assertNotType(node, "dir");
404
557
  this.#replaceNode(node, {
405
558
  type: "file",
@@ -426,274 +579,173 @@ var CachingHost = class {
426
579
  } else if (node.stub === true || node.needsFlush === false) {
427
580
  return;
428
581
  } else {
429
- return this.fs.promises.writeFile(node.fullPath, node.file);
430
- }
431
- }
432
- async #flushSymlinkNode(node) {
433
- if (!node.needsFlush)
434
- return;
435
- try {
436
- const linkValue = await this.fs.promises.readlink(node.fullPath);
437
- if (linkValue === node.symlink) {
438
- return;
439
- }
440
- } catch (e) {
441
- }
442
- return this.fs.promises.symlink(node.symlink, node.fullPath);
443
- }
444
- async #flushDirNode(node) {
445
- if (!node.tombstone && node.needsFlush) {
446
- try {
447
- await this.fs.promises.access(node.fullPath);
448
- } catch (e) {
449
- await this.fs.promises.mkdir(node.fullPath);
450
- }
451
- }
452
- const promises2 = [];
453
- for (const child of node.dir.values()) {
454
- if (node.tombstone && !child.tombstone) {
455
- throw new Error("Unexpected failure during sanity check. A non-deleted child is on a deleted dir");
456
- }
457
- if (child.type === "dir") {
458
- promises2.push(this.#flushDirNode(child));
459
- } else if (child.type === "file") {
460
- promises2.push(this.#flushFileNode(child));
461
- } else if (child.type === "symlink") {
462
- promises2.push(this.#flushSymlinkNode(child));
463
- } else {
464
- throw new Error("should never happen");
465
- }
466
- }
467
- await Promise.all(promises2);
468
- if (node.tombstone) {
469
- return this.fs.promises.rmdir(node.fullPath);
470
- }
471
- return;
472
- }
473
- flush() {
474
- const promises2 = [];
475
- for (const rootNode of this.#trees.values()) {
476
- promises2.push(this.#flushDirNode(rootNode));
477
- }
478
- return Promise.all(promises2);
479
- }
480
- };
481
-
482
- // src/matchesAnyGlob.ts
483
- import micromatch from "micromatch";
484
-
485
- // src/nanosecondsToSanity.ts
486
- function nanosecondsToSanity(n, precision = 9) {
487
- return n / BigInt(1e9) + "." + ("" + n % BigInt(1e9)).padStart(9, "0").substring(0, precision) + "s";
488
- }
489
-
490
- // src/Table.ts
491
- var Table = class {
492
- #rows = [];
493
- #config;
494
- #columnWidths = [];
495
- #footer = [];
496
- #footerRowConfig;
497
- #totalWidth = 0;
498
- constructor(config) {
499
- this.#config = {
500
- padding: 2,
501
- ...config
502
- };
503
- this.#columnWidths.fill(0, 0, config.columns.length);
504
- if (config.showFooter) {
505
- this.#footerRowConfig = [];
506
- for (const columnConfig of config.columns) {
507
- if (columnConfig.footer === void 0) {
508
- throw new Error("Must specify footer fields when showFooter is true");
509
- } else if (typeof columnConfig.footer === "string") {
510
- this.#footerRowConfig.push({
511
- type: "string",
512
- alignment: "left",
513
- aggregate: "static",
514
- value: columnConfig.footer
515
- });
516
- } else if ("value" in columnConfig.footer) {
517
- this.#footerRowConfig.push({
518
- type: "string",
519
- alignment: "left",
520
- ...columnConfig.footer
521
- });
522
- } else if ("aggregate" in columnConfig.footer) {
523
- if (columnConfig.type !== "bigint")
524
- throw new Error("expecting bigint for aggregate");
525
- this.#footerRowConfig.push({
526
- type: columnConfig.type,
527
- renderAs: columnConfig.renderAs,
528
- precision: columnConfig.precision,
529
- alignment: "right",
530
- ...columnConfig.footer
531
- });
532
- }
533
- }
534
- }
535
- }
536
- addRow(...data) {
537
- this.#rows.push(data);
538
- }
539
- #sumColumn(c) {
540
- let total = BigInt(0);
541
- for (const row of this.#rows) {
542
- total += row[c];
543
- }
544
- return total;
545
- }
546
- #updateFooterRow() {
547
- const footerRowConfig = this.#footerRowConfig;
548
- if (footerRowConfig) {
549
- for (let c = 0; c < footerRowConfig.length; c++) {
550
- const footerColConfig = footerRowConfig[c];
551
- switch (footerColConfig.aggregate) {
552
- case "sum":
553
- this.#footer[c] = this.#sumColumn(c);
554
- break;
555
- case "average":
556
- this.#footer[c] = this.#sumColumn(c) / BigInt(this.#rows.length);
557
- break;
558
- case "static":
559
- this.#footer[c] = footerColConfig.value;
560
- break;
561
- }
562
- }
563
- }
564
- }
565
- #calculateColumnWidths() {
566
- var _a;
567
- this.#columnWidths.fill(0, 0, this.#config.columns.length);
568
- for (let c = 0; c < this.#config.columns.length; c++) {
569
- const colConfig = this.#config.columns[c];
570
- this.#columnWidths[c] = Math.max(
571
- (this.#config.columns[c].header ?? "").length,
572
- ...this.#rows.map((a) => this.#getCellValueAsString(a[c], colConfig).length),
573
- this.#footer && this.#footerRowConfig ? this.#getCellValueAsString(((_a = this.#footer) == null ? void 0 : _a[c]) ?? "", this.#footerRowConfig[c]).length : 0
574
- );
575
- }
576
- this.#totalWidth = 0;
577
- for (const colWidth of this.#columnWidths) {
578
- this.#totalWidth += colWidth;
579
- }
580
- this.#totalWidth += (this.#columnWidths.length - 1) * this.#config.padding;
581
- }
582
- #printSeparator(fillString) {
583
- const paddingString = "".padStart(this.#config.padding, " ");
584
- let hr2 = "";
585
- for (let c = 0; c < this.#columnWidths.length; c++) {
586
- hr2 += "".padStart(this.#columnWidths[c], fillString) + paddingString;
587
- }
588
- hr2 = hr2.trimRight();
589
- console.log(hr2);
590
- }
591
- #printHeaderRow() {
592
- if (this.#config.showHeader) {
593
- const colConfigs = this.#config.columns;
594
- const paddingString = "".padStart(this.#config.padding, " ");
595
- let hr = "";
596
- for (let c = 0; c < colConfigs.length; c++) {
597
- const heading = colConfigs[c].header ?? "";
598
- hr += heading.padEnd(this.#columnWidths[c], " ") + paddingString;
599
- }
600
- hr = hr.trimRight();
601
- console.log(hr);
602
- this.#printSeparator("-");
603
- }
604
- }
605
- #printFooterRow() {
606
- const footerRow = this.#footer;
607
- if (footerRow) {
608
- this.#printSeparator("=");
609
- const paddingString = "".padStart(this.#config.padding, " ");
610
- let hr = "";
611
- for (let c = 0; c < footerRow.length; c++) {
612
- hr += this.#getCellValueAligned(footerRow[c], this.#footerRowConfig[c], c) + paddingString;
613
- }
614
- hr = hr.trimRight();
615
- console.log(hr);
616
- }
617
- }
618
- print() {
619
- if (this.#config.sortColumn !== void 0) {
620
- }
621
- this.#updateFooterRow();
622
- this.#calculateColumnWidths();
623
- console.log();
624
- console.log(`${this.#config.title}`);
625
- console.log("".padStart(this.#totalWidth, "="));
626
- const paddingString = "".padStart(this.#config.padding, " ");
627
- if (this.#config.showHeader) {
628
- this.#printHeaderRow();
629
- }
630
- for (let r = 0; r < this.#rows.length; r++) {
631
- let rowText = "";
632
- for (let c = 0; c < this.#config.columns.length; c++) {
633
- rowText += this.getEntryAsStringAligned(c, r) + paddingString;
634
- }
635
- rowText.trim();
636
- console.log(rowText);
582
+ return this.fs.promises.writeFile(node.fullPath, node.file);
637
583
  }
638
- if (this.#config.showFooter)
639
- this.#printFooterRow();
640
- console.log();
641
584
  }
642
- #getCellValueAsString(value, config) {
643
- if (config.type === "bigint" && config.renderAs === "nanoseconds") {
644
- return nanosecondsToSanity(value, config.precision ?? 9);
645
- } else {
646
- return "" + value;
585
+ async #flushSymlinkNode(node) {
586
+ if (!node.needsFlush) return;
587
+ try {
588
+ const linkValue = await this.fs.promises.readlink(node.fullPath);
589
+ if (linkValue === node.symlink) {
590
+ return;
591
+ }
592
+ } catch (e) {
647
593
  }
594
+ return this.fs.promises.symlink(node.symlink, node.fullPath);
648
595
  }
649
- #getCellValueAligned(value, config, column) {
650
- let result;
651
- if (config.type === "bigint" && config.renderAs === "nanoseconds") {
652
- result = nanosecondsToSanity(value, config.precision ?? 9);
653
- } else {
654
- result = "" + value;
596
+ async #flushDirNode(node) {
597
+ if (!node.tombstone && node.needsFlush) {
598
+ try {
599
+ await this.fs.promises.access(node.fullPath);
600
+ } catch (e) {
601
+ await this.fs.promises.mkdir(node.fullPath);
602
+ }
655
603
  }
656
- if (config.alignment === "left") {
657
- return result.padEnd(this.#columnWidths[column]);
658
- } else {
659
- return result.padStart(this.#columnWidths[column]);
604
+ const promises2 = [];
605
+ for (const child of node.dir.values()) {
606
+ if (node.tombstone && !child.tombstone) {
607
+ throw new Error(
608
+ "Unexpected failure during sanity check. A non-deleted child is on a deleted dir"
609
+ );
610
+ }
611
+ if (child.type === "dir") {
612
+ promises2.push(this.#flushDirNode(child));
613
+ } else if (child.type === "file") {
614
+ promises2.push(this.#flushFileNode(child));
615
+ } else if (child.type === "symlink") {
616
+ promises2.push(this.#flushSymlinkNode(child));
617
+ } else {
618
+ throw new Error("should never happen");
619
+ }
620
+ }
621
+ await Promise.all(promises2);
622
+ if (node.tombstone) {
623
+ return this.fs.promises.rmdir(node.fullPath);
660
624
  }
625
+ return;
661
626
  }
662
- getEntryAsString(colNum, rowNum) {
663
- const config = this.#config.columns[colNum];
664
- if (config.type === "bigint" && config.renderAs === "nanoseconds") {
665
- return nanosecondsToSanity(this.#rows[rowNum][colNum], config.precision ?? 9);
666
- } else {
667
- return "" + this.#rows[rowNum][colNum];
627
+ flush() {
628
+ const promises2 = [];
629
+ for (const rootNode of this.#trees.values()) {
630
+ promises2.push(this.#flushDirNode(rootNode));
668
631
  }
632
+ return Promise.all(promises2);
669
633
  }
670
- getEntryAsStringAligned(colNum, rowNum) {
671
- const config = this.#config.columns[colNum];
672
- let result;
673
- if (config.type === "bigint" && config.renderAs === "nanoseconds") {
674
- result = nanosecondsToSanity(this.#rows[rowNum][colNum], config.precision ?? 9);
675
- } else {
676
- result = "" + this.#rows[rowNum][colNum];
634
+ };
635
+
636
+ // src/findWorkspaceDir.ts
637
+ import { findUp } from "find-up";
638
+ import * as fs from "fs";
639
+ import * as path2 from "path";
640
+ async function findPnpmWorkspaceDir(cwd) {
641
+ const workspaceManifestLocation = await findUp("pnpm-workspace.yaml", {
642
+ cwd: await fs.promises.realpath(cwd)
643
+ });
644
+ return workspaceManifestLocation && path2.dirname(workspaceManifestLocation);
645
+ }
646
+ async function findWorkspaceDir(host, dir) {
647
+ const maybePnpmWorkspaceDir = await findPnpmWorkspaceDir(dir);
648
+ if (maybePnpmWorkspaceDir != null) {
649
+ return maybePnpmWorkspaceDir;
650
+ }
651
+ const packagePath = path2.join(dir, "package.json");
652
+ if (host.exists(packagePath)) {
653
+ const packageJson = host.readJson(packagePath);
654
+ if (packageJson.workspaces !== void 0) {
655
+ return dir;
677
656
  }
678
- if (config.alignment === "left") {
679
- return result.padEnd(this.#columnWidths[colNum]);
680
- } else {
681
- return result.padStart(this.#columnWidths[colNum]);
657
+ }
658
+ const nextDir = path2.normalize(path2.join(dir, ".."));
659
+ if (nextDir === dir) {
660
+ return void 0;
661
+ }
662
+ return findWorkspaceDir(host, nextDir);
663
+ }
664
+
665
+ // src/getPackageNameToDir.ts
666
+ import { join as pathJoin } from "path";
667
+
668
+ // src/getWorkspacePackageDirs.ts
669
+ import { findPackages } from "find-packages";
670
+ import { existsSync } from "fs";
671
+ import * as glob from "glob";
672
+ import * as fs2 from "node:fs";
673
+ import * as path3 from "node:path";
674
+ import readYamlFile from "read-yaml-file";
675
+ async function findPNPMWorkspacePackages(workspaceRoot) {
676
+ workspaceRoot = fs2.realpathSync(workspaceRoot);
677
+ const workspaceManifest = await readYamlFile.default(
678
+ path3.join(workspaceRoot, "pnpm-workspace.yaml")
679
+ );
680
+ return findPackages(workspaceRoot, {
681
+ ignore: ["**/node_modules/**", "**/bower_components/**"],
682
+ includeRoot: true,
683
+ patterns: workspaceManifest.packages
684
+ });
685
+ }
686
+ async function getWorkspacePackageDirs(host, workspaceDir, resolvePaths = false) {
687
+ const packageJson = host.readJson(
688
+ path3.join(workspaceDir, "package.json")
689
+ );
690
+ const isPnpmWorkspace = host.exists(
691
+ path3.join(workspaceDir, "pnpm-workspace.yaml")
692
+ );
693
+ if (isPnpmWorkspace) {
694
+ const workspacePackages = await findPNPMWorkspacePackages(workspaceDir);
695
+ if (workspacePackages.length === 0) {
696
+ throw new Error("Invalid workspaceDir: " + workspaceDir);
682
697
  }
698
+ return workspacePackages.map((project) => project.dir).filter(
699
+ (d) => d !== workspaceDir
700
+ );
683
701
  }
684
- getColumnWidth(colNum, config) {
685
- let maxWidth = Math.max(
686
- (config.header ?? "").length,
687
- this.#footer && this.#footerRowConfig ? this.#getCellValueAsString(this.#footer[colNum], this.#footerRowConfig[colNum]).length : 0
702
+ if (!packageJson.workspaces) {
703
+ throw new Error(
704
+ "Unsupported! Monorepo is not backed by either pnpm nor yarn workspaces."
688
705
  );
689
- for (let r = 0; r < this.#rows.length; r++) {
690
- maxWidth = Math.max(maxWidth, this.getEntryAsString(colNum, r).length);
706
+ }
707
+ const ret = [];
708
+ const packageGlobs = Array.isArray(packageJson.workspaces) ? packageJson.workspaces : packageJson.workspaces.packages || [];
709
+ for (const pattern of packageGlobs) {
710
+ for (const packagePath of glob.sync(pattern, { cwd: workspaceDir })) {
711
+ const packageJsonPath = path3.join(
712
+ workspaceDir,
713
+ packagePath,
714
+ "package.json"
715
+ );
716
+ if (existsSync(packageJsonPath)) {
717
+ if (resolvePaths === true) {
718
+ ret.push(path3.resolve(path3.join(workspaceDir, packagePath)));
719
+ } else {
720
+ ret.push(packagePath);
721
+ }
722
+ }
691
723
  }
692
- return maxWidth;
693
724
  }
694
- };
725
+ return ret;
726
+ }
727
+
728
+ // src/getPackageNameToDir.ts
729
+ async function getPackageNameToDir(host, workspaceDir, resolvePaths = false) {
730
+ const ret = /* @__PURE__ */ new Map();
731
+ const workspacePackages = await getWorkspacePackageDirs(
732
+ host,
733
+ workspaceDir,
734
+ resolvePaths
735
+ );
736
+ for (const packageDir of workspacePackages) {
737
+ const packagePath = pathJoin(packageDir, "package.json");
738
+ const { name } = host.readJson(packagePath);
739
+ if (name === void 0) {
740
+ throw new Error(`Package needs a name: ${packagePath}`);
741
+ }
742
+ ret.set(name, packageDir);
743
+ }
744
+ return ret;
745
+ }
695
746
 
696
747
  // src/matchesAnyGlob.ts
748
+ import micromatch from "micromatch";
697
749
  var cache = /* @__PURE__ */ new Map();
698
750
  var singleMatcherCache = /* @__PURE__ */ new Map();
699
751
  var compiledGlobCache = /* @__PURE__ */ new Map();
@@ -734,8 +786,7 @@ var matchesAnyGlob = function matchesAnyGlobFunc(needle, haystack) {
734
786
  singleMatcherHits++;
735
787
  singleMatcherSaves++;
736
788
  }
737
- if (result)
738
- break;
789
+ if (result) break;
739
790
  }
740
791
  cacheForHaystack.set(needle, result);
741
792
  } else {
@@ -764,63 +815,53 @@ matchesAnyGlob.printStats = () => {
764
815
  table.print();
765
816
  };
766
817
 
767
- // src/AggregateTiming.ts
768
- var AggregateTiming = class {
769
- constructor(title) {
770
- this.title = title;
818
+ // src/mutateJson.ts
819
+ function mutateJson(path4, host, mutator) {
820
+ let file = host.readJson(path4);
821
+ file = mutator(file);
822
+ host.writeJson(path4, file);
823
+ }
824
+
825
+ // src/SimpleHost.ts
826
+ import * as realFs2 from "fs";
827
+ var SimpleHost = class {
828
+ constructor(fs3 = realFs2) {
829
+ this.fs = fs3;
771
830
  }
772
- #data = /* @__PURE__ */ new Map();
773
- #last;
774
- start(name) {
775
- const time = process.hrtime.bigint();
776
- if (this.#last) {
777
- this.#last.total += time;
778
- }
779
- let data = this.#data.get(name);
780
- if (data === void 0) {
781
- data = { count: 1, total: -time };
782
- this.#data.set(name, data);
831
+ mkdir(directoryPath, opts) {
832
+ this.fs.mkdirSync(directoryPath, { recursive: (opts == null ? void 0 : opts.recursive) ?? false });
833
+ }
834
+ rmdir(directoryPath) {
835
+ this.fs.rmdirSync(directoryPath);
836
+ }
837
+ exists(path4) {
838
+ return this.fs.existsSync(path4);
839
+ }
840
+ writeFile(path4, body, opts) {
841
+ if (opts) {
842
+ this.fs.writeFileSync(path4, body, { encoding: opts.encoding });
783
843
  } else {
784
- data.total -= time;
785
- data.count++;
844
+ this.fs.writeFileSync(path4, body);
786
845
  }
787
- this.#last = data;
788
846
  }
789
- stop() {
790
- const time = process.hrtime.bigint();
791
- if (this.#last) {
792
- this.#last.total += time;
793
- this.#last = void 0;
847
+ readFile(path4, opts) {
848
+ if (opts == null ? void 0 : opts.asJson) {
849
+ return JSON.parse(this.fs.readFileSync(path4, "utf-8"));
794
850
  }
851
+ return this.fs.readFileSync(path4, opts == null ? void 0 : opts.encoding);
795
852
  }
796
- printResults() {
797
- const table = new Table({
798
- sortColumn: -1,
799
- showFooter: true,
800
- showHeader: true,
801
- title: this.title,
802
- columns: [
803
- {
804
- header: "Duration",
805
- type: "bigint",
806
- renderAs: "nanoseconds",
807
- footer: { aggregate: "sum" }
808
- },
809
- { header: "Task", type: "string", alignment: "left", footer: "TOTAL" },
810
- { header: "Count", type: "bigint", footer: { aggregate: "sum" } },
811
- { header: "Avg", type: "bigint", footer: { aggregate: "average" } }
812
- ]
813
- });
814
- for (const [name, value] of this.#data) {
815
- table.addRow(
816
- value.total,
817
- name,
818
- BigInt(value.count),
819
- // fixme this can be a number later
820
- value.total / BigInt(value.count)
821
- );
822
- }
823
- table.print();
853
+ deleteFile(path4) {
854
+ this.fs.unlinkSync(path4);
855
+ }
856
+ readJson(filename) {
857
+ const contents = this.fs.readFileSync(filename, "utf-8");
858
+ return JSON.parse(contents);
859
+ }
860
+ writeJson(path4, o) {
861
+ return this.fs.writeFileSync(path4, JSON.stringify(o, void 0, 2) + "\n");
862
+ }
863
+ flush() {
864
+ return Promise.resolve();
824
865
  }
825
866
  };
826
867
 
@@ -880,13 +921,13 @@ export {
880
921
  nanosecondsToSanity
881
922
  };
882
923
  /*!
883
- * Copyright 2019 Palantir Technologies, Inc.
924
+ * Copyright 2022 Palantir Technologies, Inc.
884
925
  *
885
926
  * Licensed under the MIT license. See LICENSE file in the project root for details.
886
927
  *
887
928
  */
888
929
  /*!
889
- * Copyright 2022 Palantir Technologies, Inc.
930
+ * Copyright 2019 Palantir Technologies, Inc.
890
931
  *
891
932
  * Licensed under the MIT license. See LICENSE file in the project root for details.
892
933
  *