@monorepolint/utils 0.6.0-alpha.2 → 0.6.0-alpha.4
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/.turbo/turbo-clean.log +1 -1
- package/.turbo/turbo-compile-typescript.log +1 -1
- package/.turbo/turbo-lint.log +7 -7
- package/.turbo/turbo-test.log +11 -10
- package/.turbo/turbo-transpile-typescript.log +4 -4
- package/CHANGELOG.md +4 -0
- package/build/js/index.js +489 -443
- package/build/js/index.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/CachingHost.d.ts.map +1 -1
- package/build/types/Host.d.ts.map +1 -1
- package/build/types/PackageJson.d.ts.map +1 -1
- package/build/types/SimpleHost.d.ts.map +1 -1
- package/build/types/Table.d.ts.map +1 -1
- package/build/types/findWorkspaceDir.d.ts.map +1 -1
- package/build/types/getPackageNameToDir.d.ts.map +1 -1
- package/build/types/getWorkspacePackageDirs.d.ts.map +1 -1
- package/build/types/index.d.ts +7 -7
- package/build/types/index.d.ts.map +1 -1
- package/build/types/matchesAnyGlob.d.ts.map +1 -1
- package/build/types/mutateJson.d.ts.map +1 -1
- package/build/types/nanosecondsToSanity.d.ts.map +1 -1
- package/coverage/AggregateTiming.ts.html +2 -2
- package/coverage/CachingHost.ts.html +252 -42
- package/coverage/Host.ts.html +15 -3
- package/coverage/PackageJson.ts.html +4 -4
- package/coverage/SimpleHost.ts.html +40 -7
- package/coverage/Table.ts.html +137 -29
- package/coverage/Timing.ts.html +1 -1
- package/coverage/clover.xml +527 -396
- package/coverage/coverage-final.json +13 -13
- package/coverage/findWorkspaceDir.ts.html +4 -4
- package/coverage/getPackageNameToDir.ts.html +18 -6
- package/coverage/getWorkspacePackageDirs.ts.html +168 -138
- package/coverage/index.html +40 -40
- package/coverage/index.ts.html +30 -30
- package/coverage/matchesAnyGlob.ts.html +37 -10
- package/coverage/mutateJson.ts.html +17 -5
- package/coverage/nanosecondsToSanity.ts.html +11 -5
- package/package.json +1 -2
- package/src/AggregateTiming.ts +1 -1
- package/src/CachingHost.ts +104 -34
- package/src/Host.ts +5 -1
- package/src/PackageJson.ts +3 -3
- package/src/SimpleHost.ts +14 -3
- package/src/Table.ts +58 -22
- package/src/__tests__/CachingHost.spec.ts +203 -166
- package/src/findWorkspaceDir.ts +3 -3
- package/src/getPackageNameToDir.ts +6 -2
- package/src/getWorkspacePackageDirs.ts +19 -9
- package/src/index.ts +7 -7
- package/src/matchesAnyGlob.ts +12 -3
- package/src/mutateJson.ts +5 -1
- package/src/nanosecondsToSanity.ts +3 -1
- package/vitest.config.mjs +6 -7
- package/vitest.config.mjs.timestamp-1736878329730-aa478e2241542.mjs +2 -2
package/src/CachingHost.ts
CHANGED
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import * as realFs from "node:fs";
|
|
9
|
-
import { Host } from "./Host.js";
|
|
10
9
|
import * as path from "node:path";
|
|
10
|
+
import { Host } from "./Host.js";
|
|
11
11
|
|
|
12
|
-
function assertNoTombstone(
|
|
12
|
+
function assertNoTombstone(
|
|
13
|
+
node: Node,
|
|
14
|
+
): asserts node is Node & { tombstone?: false } {
|
|
13
15
|
if (node.tombstone) {
|
|
14
16
|
throw new Error(`Unexpected tombstone ${JSON.stringify(node)}`);
|
|
15
17
|
}
|
|
@@ -17,14 +19,17 @@ function assertNoTombstone(node: Node): asserts node is Node & { tombstone?: fal
|
|
|
17
19
|
|
|
18
20
|
function assertNotType<N extends Node, T extends Node["type"]>(
|
|
19
21
|
node: N,
|
|
20
|
-
type: T
|
|
22
|
+
type: T,
|
|
21
23
|
): asserts node is N & { type: Exclude<N["type"], T> } {
|
|
22
24
|
if (node.type === type) {
|
|
23
25
|
throw new Error(`Unexpected node type ${JSON.stringify(node)}`);
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
28
|
|
|
27
|
-
function assertType<N extends Node, T extends Node["type"]>(
|
|
29
|
+
function assertType<N extends Node, T extends Node["type"]>(
|
|
30
|
+
node: N,
|
|
31
|
+
type: T,
|
|
32
|
+
): asserts node is N & { type: T } {
|
|
28
33
|
if (node.type !== type) {
|
|
29
34
|
throw new Error(`Unexpected node type ${JSON.stringify(node)}`);
|
|
30
35
|
}
|
|
@@ -90,7 +95,14 @@ interface SymlinkNode extends BaseNode<"symlink"> {
|
|
|
90
95
|
symlink: string;
|
|
91
96
|
}
|
|
92
97
|
|
|
93
|
-
type Node =
|
|
98
|
+
type Node =
|
|
99
|
+
| DirNode
|
|
100
|
+
| FileNode
|
|
101
|
+
| SymlinkNode
|
|
102
|
+
| DirTombstoneNode
|
|
103
|
+
| FileTombstoneNode
|
|
104
|
+
| DirStubNode
|
|
105
|
+
| FileStubNode;
|
|
94
106
|
|
|
95
107
|
export class CachingHost implements Host {
|
|
96
108
|
// We need many trees because of windows, key is the `root`
|
|
@@ -111,23 +123,29 @@ export class CachingHost implements Host {
|
|
|
111
123
|
| "statSync"
|
|
112
124
|
| "unlinkSync"
|
|
113
125
|
| "writeFileSync"
|
|
114
|
-
> = realFs
|
|
126
|
+
> = realFs,
|
|
115
127
|
) {}
|
|
116
128
|
|
|
117
129
|
#replaceNode(
|
|
118
130
|
node: FileNode | FileStubNode | SymlinkNode,
|
|
119
|
-
newNode: Omit<FileTombstoneNode, "fullPath" | "parent"
|
|
131
|
+
newNode: Omit<FileTombstoneNode, "fullPath" | "parent">,
|
|
120
132
|
): FileTombstoneNode;
|
|
121
133
|
#replaceNode(
|
|
122
134
|
node: FileNode | FileStubNode | FileTombstoneNode,
|
|
123
|
-
newNode: Omit<FileNode, "fullPath" | "parent"
|
|
135
|
+
newNode: Omit<FileNode, "fullPath" | "parent">,
|
|
124
136
|
): FileNode;
|
|
125
137
|
#replaceNode(
|
|
126
138
|
node: DirTombstoneNode | DirStubNode,
|
|
127
|
-
newNode: Omit<DirNode, "fullPath" | "parent" | "dir"
|
|
139
|
+
newNode: Omit<DirNode, "fullPath" | "parent" | "dir">,
|
|
128
140
|
): DirStubNode;
|
|
129
|
-
#replaceNode(
|
|
130
|
-
|
|
141
|
+
#replaceNode(
|
|
142
|
+
node: DirNode,
|
|
143
|
+
newNode: Omit<DirTombstoneNode, "fullPath" | "parent" | "dir">,
|
|
144
|
+
): DirTombstoneNode;
|
|
145
|
+
#replaceNode(
|
|
146
|
+
node: Node,
|
|
147
|
+
partialNewNode: Omit<Node, "fullPath" | "parent">,
|
|
148
|
+
): Node {
|
|
131
149
|
if (!node.parent) throw new Error("Cannot replace root node");
|
|
132
150
|
const newNode: Node = {
|
|
133
151
|
...partialNewNode,
|
|
@@ -156,15 +174,22 @@ export class CachingHost implements Host {
|
|
|
156
174
|
* Throws if the path doesnt exist!
|
|
157
175
|
*/
|
|
158
176
|
#stubify(filePath: string, parent: undefined): DirStubNode;
|
|
159
|
-
#stubify(filePath: string, parent: DirNode | DirStubNode | undefined): DirStubNode | SymlinkNode | FileStubNode;
|
|
160
177
|
#stubify(
|
|
161
178
|
filePath: string,
|
|
162
|
-
parent: DirNode | DirStubNode | undefined
|
|
163
|
-
):
|
|
179
|
+
parent: DirNode | DirStubNode | undefined,
|
|
180
|
+
): DirStubNode | SymlinkNode | FileStubNode;
|
|
181
|
+
#stubify(
|
|
182
|
+
filePath: string,
|
|
183
|
+
parent: DirNode | DirStubNode | undefined,
|
|
184
|
+
): typeof parent extends undefined ? DirNode | DirStubNode
|
|
185
|
+
: DirNode | DirStubNode | SymlinkNode | FileStubNode
|
|
186
|
+
{
|
|
164
187
|
const canonicalPath = path.resolve(filePath);
|
|
165
188
|
|
|
166
189
|
if (!parent && canonicalPath !== path.parse(canonicalPath).root) {
|
|
167
|
-
throw new Error(
|
|
190
|
+
throw new Error(
|
|
191
|
+
`parent can only be null if path is root. Instead got: ${canonicalPath}`,
|
|
192
|
+
);
|
|
168
193
|
}
|
|
169
194
|
const stat = this.fs.lstatSync(canonicalPath); // may throw
|
|
170
195
|
|
|
@@ -196,7 +221,9 @@ export class CachingHost implements Host {
|
|
|
196
221
|
needsFlush: false,
|
|
197
222
|
};
|
|
198
223
|
} else {
|
|
199
|
-
throw new Error(
|
|
224
|
+
throw new Error(
|
|
225
|
+
`what is not a file nor symlink nor directory? nothing we care about: ${canonicalPath}`,
|
|
226
|
+
);
|
|
200
227
|
}
|
|
201
228
|
|
|
202
229
|
if (!parent && node.type === "dir") {
|
|
@@ -205,7 +232,9 @@ export class CachingHost implements Host {
|
|
|
205
232
|
} else if (parent) {
|
|
206
233
|
parent.dir.set(path.basename(canonicalPath), node);
|
|
207
234
|
} else {
|
|
208
|
-
throw new Error(
|
|
235
|
+
throw new Error(
|
|
236
|
+
`root can only be a dir, got ${JSON.stringify(node)} for path: ${canonicalPath}`,
|
|
237
|
+
);
|
|
209
238
|
}
|
|
210
239
|
return node;
|
|
211
240
|
}
|
|
@@ -226,13 +255,16 @@ export class CachingHost implements Host {
|
|
|
226
255
|
}
|
|
227
256
|
|
|
228
257
|
let curPath = root;
|
|
229
|
-
let curNode: Node = this.#trees.get(root)
|
|
258
|
+
let curNode: Node = this.#trees.get(root)
|
|
259
|
+
?? this.#stubify(curPath, undefined); // its okay to throw if there is no root
|
|
230
260
|
try {
|
|
231
261
|
for (const part of parts) {
|
|
232
262
|
assertNoTombstone(curNode);
|
|
233
263
|
assertNotType(curNode, "file");
|
|
234
264
|
if (curNode.type === "symlink") {
|
|
235
|
-
const linkedNode = this.#getNodeResolvingSymlinks(
|
|
265
|
+
const linkedNode = this.#getNodeResolvingSymlinks(
|
|
266
|
+
path.resolve(path.dirname(curPath), curNode.symlink),
|
|
267
|
+
);
|
|
236
268
|
assertExists(linkedNode);
|
|
237
269
|
assertNoTombstone(linkedNode);
|
|
238
270
|
assertType(linkedNode, "dir");
|
|
@@ -240,7 +272,8 @@ export class CachingHost implements Host {
|
|
|
240
272
|
}
|
|
241
273
|
assertType(curNode, "dir");
|
|
242
274
|
assertNoTombstone(curNode);
|
|
243
|
-
curNode = curNode.dir.get(part)
|
|
275
|
+
curNode = curNode.dir.get(part)
|
|
276
|
+
?? this.#stubify(path.join(curNode.fullPath, part), curNode);
|
|
244
277
|
curPath = path.join(curPath, part);
|
|
245
278
|
}
|
|
246
279
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -253,14 +286,19 @@ export class CachingHost implements Host {
|
|
|
253
286
|
|
|
254
287
|
#getNode(filePath: string) {
|
|
255
288
|
const canonicalPath = path.resolve(filePath);
|
|
256
|
-
const { pathWithSymlinks, node } = this.#getNearestAncestorNode(
|
|
289
|
+
const { pathWithSymlinks, node } = this.#getNearestAncestorNode(
|
|
290
|
+
canonicalPath,
|
|
291
|
+
);
|
|
257
292
|
if (pathWithSymlinks === canonicalPath) {
|
|
258
293
|
return node;
|
|
259
294
|
}
|
|
260
295
|
return undefined;
|
|
261
296
|
}
|
|
262
297
|
|
|
263
|
-
#getNodeResolvingSymlinks(
|
|
298
|
+
#getNodeResolvingSymlinks(
|
|
299
|
+
filePath: string,
|
|
300
|
+
follows: number = 100,
|
|
301
|
+
): Exclude<Node, SymlinkNode> | undefined {
|
|
264
302
|
const node = this.#getNode(filePath); // canonicalizes for us
|
|
265
303
|
if (!node || node.type !== "symlink") return node;
|
|
266
304
|
// this is a really poor mans way of doing this. but who has 100's of symlinks hanging around?
|
|
@@ -269,9 +307,14 @@ export class CachingHost implements Host {
|
|
|
269
307
|
return this.#getNodeResolvingSymlinks(node.symlink, follows--);
|
|
270
308
|
}
|
|
271
309
|
|
|
272
|
-
mkdir(
|
|
310
|
+
mkdir(
|
|
311
|
+
filePath: string,
|
|
312
|
+
opts: { recursive: boolean } = { recursive: false },
|
|
313
|
+
): void {
|
|
273
314
|
const canonicalPath = path.resolve(filePath);
|
|
274
|
-
const { node, pathWithSymlinks } = this.#getNearestAncestorNode(
|
|
315
|
+
const { node, pathWithSymlinks } = this.#getNearestAncestorNode(
|
|
316
|
+
canonicalPath,
|
|
317
|
+
);
|
|
275
318
|
if (filePath === pathWithSymlinks) {
|
|
276
319
|
assertType(node, "dir");
|
|
277
320
|
assertHasParent(node);
|
|
@@ -297,7 +340,9 @@ export class CachingHost implements Host {
|
|
|
297
340
|
let maybePath = canonicalPath;
|
|
298
341
|
const toMake: string[] = [];
|
|
299
342
|
while (maybePath !== rootPath) {
|
|
300
|
-
toMake.unshift(
|
|
343
|
+
toMake.unshift(
|
|
344
|
+
path.resolve(node.fullPath, path.relative(rootPath, maybePath)),
|
|
345
|
+
);
|
|
301
346
|
maybePath = path.dirname(maybePath);
|
|
302
347
|
}
|
|
303
348
|
|
|
@@ -337,7 +382,10 @@ export class CachingHost implements Host {
|
|
|
337
382
|
readFile(filePath: string, opts: { asJson: true }): object;
|
|
338
383
|
readFile(
|
|
339
384
|
filePath: string,
|
|
340
|
-
opts: undefined | { encoding: BufferEncoding; asJson?: false } | {
|
|
385
|
+
opts: undefined | { encoding: BufferEncoding; asJson?: false } | {
|
|
386
|
+
encoding?: never;
|
|
387
|
+
asJson: true;
|
|
388
|
+
},
|
|
341
389
|
) {
|
|
342
390
|
let node = this.#getNodeResolvingSymlinks(filePath); // canonicalizes for us
|
|
343
391
|
|
|
@@ -365,10 +413,24 @@ export class CachingHost implements Host {
|
|
|
365
413
|
}
|
|
366
414
|
|
|
367
415
|
writeFile(filePath: string, buffer: Buffer): void;
|
|
368
|
-
writeFile(
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
416
|
+
writeFile(
|
|
417
|
+
filePath: string,
|
|
418
|
+
body: string,
|
|
419
|
+
opts: { encoding: BufferEncoding },
|
|
420
|
+
): void;
|
|
421
|
+
writeFile(
|
|
422
|
+
filePath: string,
|
|
423
|
+
body: string,
|
|
424
|
+
opts: { encoding: BufferEncoding },
|
|
425
|
+
): void;
|
|
426
|
+
writeFile(
|
|
427
|
+
filePath: string,
|
|
428
|
+
body: string | Buffer,
|
|
429
|
+
opts?: { encoding: BufferEncoding },
|
|
430
|
+
) {
|
|
431
|
+
const fileContentsAsBuffer = typeof body === "string"
|
|
432
|
+
? Buffer.from(body, opts?.encoding)
|
|
433
|
+
: Buffer.from(body);
|
|
372
434
|
|
|
373
435
|
const canonicalPath = path.resolve(filePath);
|
|
374
436
|
const existingNode = this.#getNodeResolvingSymlinks(canonicalPath);
|
|
@@ -384,7 +446,9 @@ export class CachingHost implements Host {
|
|
|
384
446
|
return;
|
|
385
447
|
}
|
|
386
448
|
|
|
387
|
-
const maybeDirNode = this.#getNodeResolvingSymlinks(
|
|
449
|
+
const maybeDirNode = this.#getNodeResolvingSymlinks(
|
|
450
|
+
path.dirname(canonicalPath),
|
|
451
|
+
);
|
|
388
452
|
assertExists(maybeDirNode);
|
|
389
453
|
assertType(maybeDirNode, "dir");
|
|
390
454
|
assertNoTombstone(maybeDirNode);
|
|
@@ -420,7 +484,9 @@ export class CachingHost implements Host {
|
|
|
420
484
|
});
|
|
421
485
|
}
|
|
422
486
|
|
|
423
|
-
async #flushFileNode(
|
|
487
|
+
async #flushFileNode(
|
|
488
|
+
node: FileNode | FileStubNode | FileTombstoneNode,
|
|
489
|
+
): Promise<unknown> {
|
|
424
490
|
// FIXME all tombstones need a flush, so we can get rid of needsFlush for them
|
|
425
491
|
if (node.tombstone) {
|
|
426
492
|
try {
|
|
@@ -453,7 +519,9 @@ export class CachingHost implements Host {
|
|
|
453
519
|
return this.fs.promises.symlink(node.symlink, node.fullPath);
|
|
454
520
|
}
|
|
455
521
|
|
|
456
|
-
async #flushDirNode(
|
|
522
|
+
async #flushDirNode(
|
|
523
|
+
node: DirNode | DirStubNode | DirTombstoneNode,
|
|
524
|
+
): Promise<unknown> {
|
|
457
525
|
if (!node.tombstone && node.needsFlush) {
|
|
458
526
|
try {
|
|
459
527
|
await this.fs.promises.access(node.fullPath); // throws if the file doesnt exist
|
|
@@ -466,7 +534,9 @@ export class CachingHost implements Host {
|
|
|
466
534
|
const promises: Promise<unknown>[] = [];
|
|
467
535
|
for (const child of node.dir.values()) {
|
|
468
536
|
if (node.tombstone && !child.tombstone) {
|
|
469
|
-
throw new Error(
|
|
537
|
+
throw new Error(
|
|
538
|
+
"Unexpected failure during sanity check. A non-deleted child is on a deleted dir",
|
|
539
|
+
);
|
|
470
540
|
}
|
|
471
541
|
if (child.type === "dir") {
|
|
472
542
|
promises.push(this.#flushDirNode(child));
|
package/src/Host.ts
CHANGED
|
@@ -19,7 +19,11 @@ export interface Host {
|
|
|
19
19
|
writeJson(path: string, o: object): void;
|
|
20
20
|
|
|
21
21
|
writeFile(path: string, buffer: Buffer): void;
|
|
22
|
-
writeFile(
|
|
22
|
+
writeFile(
|
|
23
|
+
path: string,
|
|
24
|
+
body: string,
|
|
25
|
+
opts: { encoding: BufferEncoding },
|
|
26
|
+
): void;
|
|
23
27
|
|
|
24
28
|
readFile(path: string, opts?: undefined): Buffer;
|
|
25
29
|
readFile(path: string, opts: { encoding: BufferEncoding }): string;
|
package/src/PackageJson.ts
CHANGED
|
@@ -14,9 +14,9 @@ export interface PackageJson {
|
|
|
14
14
|
optionalDependencies?: Record<string, string>;
|
|
15
15
|
workspaces?:
|
|
16
16
|
| {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
packages?: string[];
|
|
18
|
+
nohoist?: string[];
|
|
19
|
+
}
|
|
20
20
|
| string[];
|
|
21
21
|
[otherKey: string]: unknown;
|
|
22
22
|
}
|
package/src/SimpleHost.ts
CHANGED
|
@@ -22,8 +22,16 @@ export class SimpleHost implements Host {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
writeFile(path: string, buffer: Buffer): void;
|
|
25
|
-
writeFile(
|
|
26
|
-
|
|
25
|
+
writeFile(
|
|
26
|
+
path: string,
|
|
27
|
+
body: string,
|
|
28
|
+
opts: { encoding: BufferEncoding },
|
|
29
|
+
): void;
|
|
30
|
+
writeFile(
|
|
31
|
+
path: string,
|
|
32
|
+
body: string | Buffer,
|
|
33
|
+
opts?: { encoding: BufferEncoding },
|
|
34
|
+
): void {
|
|
27
35
|
if (opts) {
|
|
28
36
|
this.fs.writeFileSync(path, body, { encoding: opts.encoding });
|
|
29
37
|
} else {
|
|
@@ -33,7 +41,10 @@ export class SimpleHost implements Host {
|
|
|
33
41
|
readFile(path: string, opts?: undefined): Buffer;
|
|
34
42
|
readFile(path: string, opts: { encoding: BufferEncoding }): string;
|
|
35
43
|
readFile(path: string, opts: { asJson: true }): object;
|
|
36
|
-
readFile(
|
|
44
|
+
readFile(
|
|
45
|
+
path: string,
|
|
46
|
+
opts?: { encoding?: BufferEncoding; asJson?: boolean },
|
|
47
|
+
): string | object | Buffer {
|
|
37
48
|
if (opts?.asJson) {
|
|
38
49
|
return JSON.parse(this.fs.readFileSync(path, "utf-8"));
|
|
39
50
|
}
|
package/src/Table.ts
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
// tslint:disable:no-console
|
|
8
8
|
import { nanosecondsToSanity } from "./nanosecondsToSanity.js";
|
|
9
|
-
type HeaderFooterHelper<HB, FB, H, F> =
|
|
10
|
-
(
|
|
9
|
+
type HeaderFooterHelper<HB, FB, H, F> =
|
|
10
|
+
& (HB extends true ? { header: H } : { header?: H })
|
|
11
|
+
& (FB extends true ? { footer: F } : { footer?: F });
|
|
11
12
|
|
|
12
13
|
type WithAlignemnt = { alignment?: "right" | "left" };
|
|
13
14
|
type BaseCellConfig = WithAlignemnt & { type: "bigint" | "string" };
|
|
@@ -19,13 +20,20 @@ type BaseBigIntCellConfig = {
|
|
|
19
20
|
} & WithAlignemnt;
|
|
20
21
|
type BaseStringCellConfig = { type: "string" } & WithAlignemnt;
|
|
21
22
|
|
|
22
|
-
type BigIntColumnConfig<H, F> =
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
type BigIntColumnConfig<H, F> =
|
|
24
|
+
& WithAlignemnt
|
|
25
|
+
& BaseBigIntCellConfig
|
|
26
|
+
& HeaderFooterHelper<
|
|
27
|
+
H,
|
|
28
|
+
F,
|
|
29
|
+
string,
|
|
30
|
+
AggregateFooterConfig | StaticFooterConfig
|
|
31
|
+
>;
|
|
32
|
+
|
|
33
|
+
type StringColumnConfig<H, F> =
|
|
34
|
+
& WithAlignemnt
|
|
35
|
+
& BaseStringCellConfig
|
|
36
|
+
& HeaderFooterHelper<H, F, string, StaticFooterConfig>;
|
|
29
37
|
|
|
30
38
|
type AggregateFooterConfig = {
|
|
31
39
|
aggregate: "sum" | "average";
|
|
@@ -47,7 +55,8 @@ type TableConfig<T extends any[], H extends boolean, F extends boolean> = {
|
|
|
47
55
|
showHeader: H;
|
|
48
56
|
showFooter: F;
|
|
49
57
|
columns: {
|
|
50
|
-
[K in keyof T]: T[K] extends bigint ? BigIntColumnConfig<H, F>
|
|
58
|
+
[K in keyof T]: T[K] extends bigint ? BigIntColumnConfig<H, F>
|
|
59
|
+
: StringColumnConfig<H, F>;
|
|
51
60
|
};
|
|
52
61
|
title: string;
|
|
53
62
|
};
|
|
@@ -74,7 +83,7 @@ export class Table<T extends any[]> {
|
|
|
74
83
|
| TableConfig<T, true, true>
|
|
75
84
|
| TableConfig<T, true, false>
|
|
76
85
|
| TableConfig<T, false, true>
|
|
77
|
-
| TableConfig<T, false, false
|
|
86
|
+
| TableConfig<T, false, false>,
|
|
78
87
|
) {
|
|
79
88
|
this.#config = {
|
|
80
89
|
padding: 2,
|
|
@@ -101,7 +110,9 @@ export class Table<T extends any[]> {
|
|
|
101
110
|
...columnConfig.footer,
|
|
102
111
|
});
|
|
103
112
|
} else if ("aggregate" in columnConfig.footer) {
|
|
104
|
-
if (columnConfig.type !== "bigint")
|
|
113
|
+
if (columnConfig.type !== "bigint") {
|
|
114
|
+
throw new Error("expecting bigint for aggregate");
|
|
115
|
+
}
|
|
105
116
|
this.#footerRowConfig.push({
|
|
106
117
|
type: columnConfig.type,
|
|
107
118
|
renderAs: columnConfig.renderAs,
|
|
@@ -157,8 +168,11 @@ export class Table<T extends any[]> {
|
|
|
157
168
|
(this.#config.columns[c].header ?? "").length,
|
|
158
169
|
...this.#rows.map((a) => this.#getCellValueAsString(a[c], colConfig).length),
|
|
159
170
|
this.#footer && this.#footerRowConfig
|
|
160
|
-
? this.#getCellValueAsString(
|
|
161
|
-
|
|
171
|
+
? this.#getCellValueAsString(
|
|
172
|
+
this.#footer?.[c] ?? "",
|
|
173
|
+
this.#footerRowConfig[c],
|
|
174
|
+
).length
|
|
175
|
+
: 0,
|
|
162
176
|
);
|
|
163
177
|
}
|
|
164
178
|
|
|
@@ -208,7 +222,8 @@ export class Table<T extends any[]> {
|
|
|
208
222
|
|
|
209
223
|
let hr = "";
|
|
210
224
|
for (let c = 0; c < footerRow.length; c++) {
|
|
211
|
-
hr += this.#getCellValueAligned(footerRow[c], this.#footerRowConfig![c], c)
|
|
225
|
+
hr += this.#getCellValueAligned(footerRow[c], this.#footerRowConfig![c], c)
|
|
226
|
+
+ paddingString; // .padEnd(this.#columnWidths[c], " ") + paddingString;
|
|
212
227
|
}
|
|
213
228
|
hr = hr.trimRight();
|
|
214
229
|
console.log(hr);
|
|
@@ -246,7 +261,10 @@ export class Table<T extends any[]> {
|
|
|
246
261
|
console.log();
|
|
247
262
|
}
|
|
248
263
|
|
|
249
|
-
#getCellValueAsString(
|
|
264
|
+
#getCellValueAsString(
|
|
265
|
+
value: bigint | string,
|
|
266
|
+
config: BaseBigIntCellConfig | BaseStringCellConfig,
|
|
267
|
+
) {
|
|
250
268
|
if (config.type === "bigint" && config.renderAs === "nanoseconds") {
|
|
251
269
|
return nanosecondsToSanity(value as bigint, config.precision ?? 9);
|
|
252
270
|
} else {
|
|
@@ -254,7 +272,11 @@ export class Table<T extends any[]> {
|
|
|
254
272
|
}
|
|
255
273
|
}
|
|
256
274
|
|
|
257
|
-
#getCellValueAligned(
|
|
275
|
+
#getCellValueAligned(
|
|
276
|
+
value: bigint | string,
|
|
277
|
+
config: BaseBigIntCellConfig | BaseStringCellConfig,
|
|
278
|
+
column: number,
|
|
279
|
+
) {
|
|
258
280
|
let result: string;
|
|
259
281
|
if (config.type === "bigint" && config.renderAs === "nanoseconds") {
|
|
260
282
|
result = nanosecondsToSanity(value as bigint, config.precision ?? 9);
|
|
@@ -273,7 +295,10 @@ export class Table<T extends any[]> {
|
|
|
273
295
|
const config = this.#config.columns[colNum];
|
|
274
296
|
|
|
275
297
|
if (config.type === "bigint" && config.renderAs === "nanoseconds") {
|
|
276
|
-
return nanosecondsToSanity(
|
|
298
|
+
return nanosecondsToSanity(
|
|
299
|
+
this.#rows[rowNum][colNum],
|
|
300
|
+
config.precision ?? 9,
|
|
301
|
+
);
|
|
277
302
|
} else {
|
|
278
303
|
return "" + this.#rows[rowNum][colNum];
|
|
279
304
|
}
|
|
@@ -284,7 +309,10 @@ export class Table<T extends any[]> {
|
|
|
284
309
|
|
|
285
310
|
let result: string;
|
|
286
311
|
if (config.type === "bigint" && config.renderAs === "nanoseconds") {
|
|
287
|
-
result = nanosecondsToSanity(
|
|
312
|
+
result = nanosecondsToSanity(
|
|
313
|
+
this.#rows[rowNum][colNum],
|
|
314
|
+
config.precision ?? 9,
|
|
315
|
+
);
|
|
288
316
|
} else {
|
|
289
317
|
result = "" + this.#rows[rowNum][colNum];
|
|
290
318
|
}
|
|
@@ -296,12 +324,20 @@ export class Table<T extends any[]> {
|
|
|
296
324
|
}
|
|
297
325
|
}
|
|
298
326
|
|
|
299
|
-
getColumnWidth(
|
|
327
|
+
getColumnWidth(
|
|
328
|
+
colNum: number,
|
|
329
|
+
config:
|
|
330
|
+
| BigIntColumnConfig<boolean, boolean>
|
|
331
|
+
| StringColumnConfig<boolean, boolean>,
|
|
332
|
+
) {
|
|
300
333
|
let maxWidth = Math.max(
|
|
301
334
|
(config.header ?? "").length,
|
|
302
335
|
this.#footer && this.#footerRowConfig
|
|
303
|
-
? this.#getCellValueAsString(
|
|
304
|
-
|
|
336
|
+
? this.#getCellValueAsString(
|
|
337
|
+
this.#footer[colNum],
|
|
338
|
+
this.#footerRowConfig[colNum],
|
|
339
|
+
).length
|
|
340
|
+
: 0,
|
|
305
341
|
);
|
|
306
342
|
|
|
307
343
|
for (let r = 0; r < this.#rows.length; r++) {
|