Skip to content

Commit 8efcdb8

Browse files
committed
refactor: re-enable eslint rules
1 parent 32597dd commit 8efcdb8

File tree

2 files changed

+66
-126
lines changed

2 files changed

+66
-126
lines changed

src/ux/table.ts

Lines changed: 63 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,9 @@ import jsyaml from 'js-yaml';
1010
import { orderBy } from 'natural-orderby';
1111
import sliceAnsi from 'slice-ansi';
1212
import sw from 'string-width';
13-
import { Interfaces, Flags as F } from '@oclif/core';
1413

1514
import write from './write.js';
1615

17-
/* eslint-disable @typescript-eslint/no-explicit-any */
18-
/* eslint-disable @typescript-eslint/explicit-function-return-type */
19-
/* eslint-disable @typescript-eslint/no-unsafe-return */
20-
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
21-
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
22-
/* eslint-disable @typescript-eslint/no-unsafe-argument */
23-
2416
function sumBy<T>(arr: T[], fn: (i: T) => number): number {
2517
return arr.reduce((sum, i) => sum + fn(i), 0);
2618
}
@@ -49,17 +41,19 @@ function termwidth(stream: NodeJS.WriteStream): number {
4941
const stdtermwidth = Number.parseInt(process.env.OCLIF_COLUMNS!, 10) || termwidth(process.stdout);
5042

5143
class Table<T extends Record<string, unknown>> {
52-
private columns: Array<table.Column<T> & { key: string; maxWidth?: number; width?: number }>;
44+
private columns: Array<Column<T> & { key: string; maxWidth?: number; width?: number }>;
45+
46+
private options: Options & { printLine: (s: unknown) => void };
5347

54-
private options: table.Options & { printLine(s: any): any };
48+
private data: Array<Record<string, unknown>>;
5549

56-
public constructor(private data: T[], columns: table.Columns<T>, options: table.Options = {}) {
50+
public constructor(data: T[], columns: Columns<T>, options: Options = {}) {
5751
// assign columns
5852
this.columns = Object.keys(columns).map((key: string) => {
5953
const col = columns[key];
6054
const extended = col.extended ?? false;
6155
// turn null and undefined into empty strings by default
62-
const get = col.get ?? ((row: any) => row[key] ?? '');
56+
const get = col.get ?? ((row: Record<string, unknown>): unknown => row[key] ?? '');
6357
const header = typeof col.header === 'string' ? col.header : capitalize(key.replaceAll('_', ' '));
6458
const minWidth = Math.max(col.minWidth ?? 0, sw(header) + 1);
6559

@@ -87,20 +81,17 @@ class Table<T extends Record<string, unknown>> {
8781
sort,
8882
title,
8983
};
90-
}
9184

92-
public display() {
9385
// build table rows from input array data
94-
let rows = this.data.map((d) => {
95-
const row: any = {};
96-
for (const col of this.columns) {
97-
let val = col.get(d);
98-
if (typeof val !== 'string') val = inspect(val, { breakLength: Number.POSITIVE_INFINITY });
99-
row[col.key] = val;
100-
}
101-
102-
return row;
103-
});
86+
let rows = data.map((d) =>
87+
Object.fromEntries(
88+
this.columns.map((col) => {
89+
let val = col.get(d);
90+
if (typeof val !== 'string') val = inspect(val, { breakLength: Number.POSITIVE_INFINITY });
91+
return [col.key, val];
92+
})
93+
)
94+
);
10495

10596
// filter rows
10697
if (this.options.filter) {
@@ -110,7 +101,7 @@ class Table<T extends Record<string, unknown>> {
110101
if (isNot) header = header.slice(1);
111102
const col = this.findColumnFromHeader(header);
112103
if (!col || !regex) throw new Error('Filter flag has an invalid value');
113-
rows = rows.filter((d: any) => {
104+
rows = rows.filter((d) => {
114105
const re = new RegExp(regex);
115106
const val = d[col.key] as string;
116107
const match = val.match(re);
@@ -122,7 +113,11 @@ class Table<T extends Record<string, unknown>> {
122113
if (this.options.sort) {
123114
const sorters = this.options.sort.split(',');
124115
const sortHeaders = sorters.map((k) => (k.startsWith('-') ? k.slice(1) : k));
125-
const sortKeys = this.filterColumnsFromHeaders(sortHeaders).map((c) => (v: any) => v[c.key]);
116+
const sortKeys = this.filterColumnsFromHeaders(sortHeaders).map(
117+
(c) =>
118+
(v: Record<string, unknown>): unknown =>
119+
v[c.key]
120+
);
126121
const sortKeysOrder = sorters.map((k) => (k.startsWith('-') ? 'desc' : 'asc'));
127122
rows = orderBy(rows, sortKeys, sortKeysOrder);
128123
}
@@ -137,7 +132,9 @@ class Table<T extends Record<string, unknown>> {
137132
}
138133

139134
this.data = rows;
135+
}
140136

137+
public display(): void {
141138
switch (this.options.output) {
142139
case 'csv': {
143140
this.outputCSV();
@@ -162,11 +159,9 @@ class Table<T extends Record<string, unknown>> {
162159

163160
private filterColumnsFromHeaders(
164161
filters: string[]
165-
): Array<table.Column<T> & { key: string; maxWidth?: number; width?: number }> {
166-
// unique
167-
filters = [...new Set(filters)];
168-
const cols: Array<table.Column<T> & { key: string; maxWidth?: number; width?: number }> = [];
169-
for (const f of filters) {
162+
): Array<Column<T> & { key: string; maxWidth?: number; width?: number }> {
163+
const cols: Array<Column<T> & { key: string; maxWidth?: number; width?: number }> = [];
164+
for (const f of [...new Set(filters)]) {
170165
const c = this.columns.find((i) => i.header.toLowerCase() === f.toLowerCase());
171166
if (c) cols.push(c);
172167
}
@@ -176,19 +171,19 @@ class Table<T extends Record<string, unknown>> {
176171

177172
private findColumnFromHeader(
178173
header: string
179-
): (table.Column<T> & { key: string; maxWidth?: number; width?: number }) | undefined {
174+
): (Column<T> & { key: string; maxWidth?: number; width?: number }) | undefined {
180175
return this.columns.find((c) => c.header.toLowerCase() === header.toLowerCase());
181176
}
182177

183-
private getCSVRow(d: any): string[] {
178+
private getCSVRow(d: Record<string, unknown>): string[] {
184179
const values = this.columns.map((col) => d[col.key] || '') as string[];
185180
const lineToBeEscaped = values.find(
186181
(e: string) => e.includes('"') || e.includes('\n') || e.includes('\r\n') || e.includes('\r') || e.includes(',')
187182
);
188183
return values.map((e) => (lineToBeEscaped ? `"${e.replaceAll('"', '""')}"` : e));
189184
}
190185

191-
private outputCSV() {
186+
private outputCSV(): void {
192187
const { columns, data, options } = this;
193188

194189
if (!options['no-header']) {
@@ -201,11 +196,11 @@ class Table<T extends Record<string, unknown>> {
201196
}
202197
}
203198

204-
private outputJSON() {
199+
private outputJSON(): void {
205200
this.options.printLine(JSON.stringify(this.resolveColumnsToObjectArray(), undefined, 2));
206201
}
207202

208-
private outputTable() {
203+
private outputTable(): void {
209204
const { data, options } = this;
210205
// column truncation
211206
//
@@ -222,7 +217,7 @@ class Table<T extends Record<string, unknown>> {
222217
// terminal width
223218
const maxWidth = stdtermwidth - 2;
224219
// truncation logic
225-
const shouldShorten = () => {
220+
const shouldShorten = (): void => {
226221
// don't shorten if full mode
227222
if (options['no-truncate'] ?? (!process.stdout.isTTY && !process.env.CLI_UX_SKIP_TTY_CHECK)) return;
228223

@@ -304,7 +299,7 @@ class Table<T extends Record<string, unknown>> {
304299
// with multi-line strings
305300
let numOfLines = 1;
306301
for (const col of columns) {
307-
const d = (row as any)[col.key] as string;
302+
const d = row[col.key] as string;
308303
const lines = d.split('\n').length;
309304
if (lines > numOfLines) numOfLines = lines;
310305
}
@@ -318,7 +313,7 @@ class Table<T extends Record<string, unknown>> {
318313
let l = options.rowStart;
319314
for (const col of columns) {
320315
const width = col.width;
321-
let d = (row as any)[col.key] as string;
316+
let d = row[col.key] as string;
322317
d = d.split('\n')[i] || '';
323318
const visualWidth = sw(d);
324319
const colorWidth = d.length - visualWidth;
@@ -344,100 +339,45 @@ class Table<T extends Record<string, unknown>> {
344339
this.options.printLine(jsyaml.dump(this.resolveColumnsToObjectArray()));
345340
}
346341

347-
private resolveColumnsToObjectArray(): Array<{ [k: string]: any }> {
342+
private resolveColumnsToObjectArray(): Array<Record<string, unknown>> {
348343
const { columns, data } = this;
349-
return data.map((d: any) => Object.fromEntries(columns.map((col) => [col.key, d[col.key] ?? ''])));
344+
return data.map((d) => Object.fromEntries(columns.map((col) => [col.key, d[col.key] ?? ''])));
350345
}
351346
}
352347

353-
export function table<T extends Record<string, unknown>>(
354-
data: T[],
355-
columns: table.Columns<T>,
356-
options: table.Options = {}
357-
): void {
348+
export function table<T extends Record<string, unknown>>(data: T[], columns: Columns<T>, options: Options = {}): void {
358349
new Table(data, columns, options).display();
359350
}
360351

361-
export namespace table {
362-
export const Flags: {
363-
columns: Interfaces.OptionFlag<string | undefined>;
364-
csv: Interfaces.BooleanFlag<boolean>;
365-
extended: Interfaces.BooleanFlag<boolean>;
366-
filter: Interfaces.OptionFlag<string | undefined>;
367-
'no-header': Interfaces.BooleanFlag<boolean>;
368-
'no-truncate': Interfaces.BooleanFlag<boolean>;
369-
output: Interfaces.OptionFlag<string | undefined>;
370-
sort: Interfaces.OptionFlag<string | undefined>;
371-
} = {
372-
columns: F.string({ description: 'only show provided columns (comma-separated)', exclusive: ['extended'] }),
373-
csv: F.boolean({ description: 'output is csv format [alias: --output=csv]', exclusive: ['no-truncate'] }),
374-
extended: F.boolean({ char: 'x', description: 'show extra columns', exclusive: ['columns'] }),
375-
filter: F.string({ description: 'filter property by partial string matching, ex: name=foo' }),
376-
'no-header': F.boolean({ description: 'hide table header from output', exclusive: ['csv'] }),
377-
'no-truncate': F.boolean({ description: 'do not truncate output to fit screen', exclusive: ['csv'] }),
378-
output: F.string({
379-
description: 'output in a more machine friendly format',
380-
exclusive: ['no-truncate', 'csv'],
381-
options: ['csv', 'json', 'yaml'],
382-
}),
383-
sort: F.string({ description: "property to sort by (prepend '-' for descending)" }),
384-
};
385-
386-
type IFlags = typeof Flags;
387-
type ExcludeFlags<T, Z> = Pick<T, Exclude<keyof T, Z>>;
388-
type IncludeFlags<T, K extends keyof T> = Pick<T, K>;
389-
390-
export function flags(): IFlags;
391-
export function flags<Z extends keyof IFlags = keyof IFlags>(opts: { except: Z | Z[] }): ExcludeFlags<IFlags, Z>;
392-
export function flags<K extends keyof IFlags = keyof IFlags>(opts: { only: K | K[] }): IncludeFlags<IFlags, K>;
393-
394-
export function flags(opts?: any): any {
395-
if (opts) {
396-
const f = {};
397-
const o = (opts.only && typeof opts.only === 'string' ? [opts.only] : opts.only) || Object.keys(Flags);
398-
const e = (opts.except && typeof opts.except === 'string' ? [opts.except] : opts.except) || [];
399-
for (const key of o) {
400-
if (!(e as any[]).includes(key)) {
401-
(f as any)[key] = (Flags as any)[key];
402-
}
403-
}
404-
405-
return f;
406-
}
407-
408-
return Flags;
409-
}
410-
411-
export type Column<T extends Record<string, unknown>> = {
412-
extended: boolean;
413-
header: string;
414-
minWidth: number;
415-
get(row: T): unknown;
416-
};
417-
418-
export type Columns<T extends Record<string, unknown>> = { [key: string]: Partial<Column<T>> };
419-
420-
// export type OutputType = 'csv' | 'json' | 'yaml'
421-
422-
export type Options = {
423-
[key: string]: any;
424-
columns?: string;
425-
extended?: boolean;
426-
filter?: string;
427-
'no-header'?: boolean;
428-
'no-truncate'?: boolean;
429-
output?: string;
430-
sort?: string;
431-
title?: string;
432-
printLine?(s: any): any;
433-
};
434-
}
435-
436-
const getWidestColumnWith = (data: any[], columnKey: string): number =>
352+
export type Column<T extends Record<string, unknown>> = {
353+
extended: boolean;
354+
header: string;
355+
minWidth: number;
356+
get(row: T): unknown;
357+
};
358+
359+
export type Columns<T extends Record<string, unknown>> = { [key: string]: Partial<Column<T>> };
360+
361+
export type Options = {
362+
[key: string]: unknown;
363+
columns?: string;
364+
extended?: boolean;
365+
filter?: string;
366+
'no-header'?: boolean;
367+
'no-truncate'?: boolean;
368+
output?: string;
369+
rowStart?: string;
370+
sort?: string;
371+
title?: string;
372+
printLine?(s: unknown): void;
373+
};
374+
375+
const getWidestColumnWith = (data: Array<Record<string, unknown>>, columnKey: string): number =>
437376
data.reduce((previous, current) => {
438377
const d = current[columnKey];
378+
if (typeof d !== 'string') return previous;
439379
// convert multi-line cell to single longest line
440380
// for width calculations
441-
const manyLines = (d as string).split('\n');
381+
const manyLines = d.split('\n');
442382
return Math.max(previous, manyLines.length > 1 ? Math.max(...manyLines.map((r: string) => sw(r))) : sw(d));
443383
}, 0);

src/ux/ux.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { AnyJson } from '@salesforce/ts-types';
1111
import terminalLink from 'terminal-link';
1212
import { UxBase } from './base.js';
1313
import { Spinner } from './spinner.js';
14-
import { table } from './table.js';
14+
import { table, Columns as TableColumns, Options as TableOptions } from './table.js';
1515
import styledJSON from './styledJSON.js';
1616
import styledObject from './styledObject.js';
1717
import write from './write.js';
@@ -125,7 +125,7 @@ export class Ux extends UxBase {
125125
export namespace Ux {
126126
export namespace Table {
127127
export type Data = Record<string, unknown>;
128-
export type Columns<T extends Data> = table.Columns<T>;
129-
export type Options = table.Options;
128+
export type Columns<T extends Data> = TableColumns<T>;
129+
export type Options = TableOptions;
130130
}
131131
}

0 commit comments

Comments
 (0)