@oclif/table 0.1.18 → 0.1.19
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/lib/table.js +74 -3
- package/lib/utils.js +6 -4
- package/package.json +3 -3
package/lib/table.js
CHANGED
|
@@ -121,7 +121,7 @@ export function formatTextWithMargins({ horizontalAlignment, overflow, padding,
|
|
|
121
121
|
...calculateMargins(spaces),
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
|
-
|
|
124
|
+
function setup(props) {
|
|
125
125
|
const { data, filter, horizontalAlignment = 'left', maxWidth, noStyle = false, overflow = 'truncate', padding = 1, sort, title, verticalAlignment = 'top', } = props;
|
|
126
126
|
const headerOptions = noStyle ? {} : { bold: true, color: 'blue', ...props.headerOptions };
|
|
127
127
|
const borderStyle = noStyle ? 'none' : (props.borderStyle ?? 'all');
|
|
@@ -142,6 +142,12 @@ export function Table(props) {
|
|
|
142
142
|
};
|
|
143
143
|
const headings = getHeadings(config);
|
|
144
144
|
const columns = getColumns(config, headings);
|
|
145
|
+
// check for duplicate columns
|
|
146
|
+
const columnKeys = columns.map((c) => c.key);
|
|
147
|
+
const duplicates = columnKeys.filter((c, i) => columnKeys.indexOf(c) !== i);
|
|
148
|
+
if (duplicates.length > 0) {
|
|
149
|
+
throw new Error(`Duplicate columns found: ${duplicates.join(', ')}`);
|
|
150
|
+
}
|
|
145
151
|
const dataComponent = row({
|
|
146
152
|
borderProps,
|
|
147
153
|
cell: Cell,
|
|
@@ -180,6 +186,23 @@ export function Table(props) {
|
|
|
180
186
|
props: borderProps,
|
|
181
187
|
skeleton: BORDER_SKELETONS[config.borderStyle].separator,
|
|
182
188
|
});
|
|
189
|
+
return {
|
|
190
|
+
columns,
|
|
191
|
+
config,
|
|
192
|
+
dataComponent,
|
|
193
|
+
footerComponent,
|
|
194
|
+
headerComponent,
|
|
195
|
+
headerFooterComponent,
|
|
196
|
+
headingComponent,
|
|
197
|
+
headings,
|
|
198
|
+
processedData,
|
|
199
|
+
separatorComponent,
|
|
200
|
+
title,
|
|
201
|
+
titleOptions,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
export function Table(props) {
|
|
205
|
+
const { columns, config, dataComponent, footerComponent, headerComponent, headerFooterComponent, headingComponent, headings, processedData, separatorComponent, title, titleOptions, } = setup(props);
|
|
183
206
|
return (React.createElement(Box, { flexDirection: "column", width: determineWidthToUse(columns, config.maxWidth) },
|
|
184
207
|
title && React.createElement(Text, { ...titleOptions }, title),
|
|
185
208
|
headerComponent({ columns, data: {}, key: 'header' }),
|
|
@@ -264,7 +287,12 @@ export function Skeleton(props) {
|
|
|
264
287
|
class Stream extends WriteStream {
|
|
265
288
|
frames = [];
|
|
266
289
|
lastFrame() {
|
|
267
|
-
return this.frames
|
|
290
|
+
return this.frames
|
|
291
|
+
.filter((f) => {
|
|
292
|
+
const stripped = stripAnsi(f);
|
|
293
|
+
return stripped !== '' && stripped !== '\n';
|
|
294
|
+
})
|
|
295
|
+
.at(-1);
|
|
268
296
|
}
|
|
269
297
|
write(data) {
|
|
270
298
|
this.frames.push(data);
|
|
@@ -278,18 +306,58 @@ class Output {
|
|
|
278
306
|
}
|
|
279
307
|
maybePrintLastFrame() {
|
|
280
308
|
if (this.stream instanceof Stream) {
|
|
281
|
-
process.stdout.write(`${this.stream.lastFrame()}
|
|
309
|
+
process.stdout.write(`${this.stream.lastFrame()}`);
|
|
282
310
|
}
|
|
283
311
|
else {
|
|
284
312
|
process.stdout.write('\n');
|
|
285
313
|
}
|
|
286
314
|
}
|
|
287
315
|
}
|
|
316
|
+
function chunk(array, size) {
|
|
317
|
+
return array.reduce((acc, _, i) => {
|
|
318
|
+
if (i % size === 0)
|
|
319
|
+
acc.push(array.slice(i, i + size));
|
|
320
|
+
return acc;
|
|
321
|
+
}, []);
|
|
322
|
+
}
|
|
323
|
+
function renderTableInChunks(props) {
|
|
324
|
+
const { columns, config, dataComponent, footerComponent, headerComponent, headerFooterComponent, headingComponent, headings, processedData, separatorComponent, title, titleOptions, } = setup(props);
|
|
325
|
+
const headerOutput = new Output();
|
|
326
|
+
const headerInstance = render(React.createElement(Box, { flexDirection: "column", width: determineWidthToUse(columns, config.maxWidth) },
|
|
327
|
+
title && React.createElement(Text, { ...titleOptions }, title),
|
|
328
|
+
headerComponent({ columns, data: {}, key: 'header' }),
|
|
329
|
+
headingComponent({ columns, data: headings, key: 'heading' }),
|
|
330
|
+
headerFooterComponent({ columns, data: {}, key: 'footer' })), { stdout: headerOutput.stream });
|
|
331
|
+
headerInstance.unmount();
|
|
332
|
+
headerOutput.maybePrintLastFrame();
|
|
333
|
+
const chunks = chunk(processedData, Math.max(1, Math.floor(process.stdout.rows / 2)));
|
|
334
|
+
for (const chunk of chunks) {
|
|
335
|
+
const chunkOutput = new Output();
|
|
336
|
+
const instance = render(React.createElement(Box, { flexDirection: "column", width: determineWidthToUse(columns, config.maxWidth) }, chunk.map((row, index) => {
|
|
337
|
+
// Calculate the hash of the row based on its value and position
|
|
338
|
+
const key = `row-${sha1(row)}-${index}`;
|
|
339
|
+
// Construct a row.
|
|
340
|
+
return (React.createElement(Box, { key: key, flexDirection: "column" },
|
|
341
|
+
separatorComponent({ columns, data: {}, key: `separator-${key}` }),
|
|
342
|
+
dataComponent({ columns, data: row, key: `data-${key}` })));
|
|
343
|
+
})), { stdout: chunkOutput.stream });
|
|
344
|
+
instance.unmount();
|
|
345
|
+
chunkOutput.maybePrintLastFrame();
|
|
346
|
+
}
|
|
347
|
+
const footerOutput = new Output();
|
|
348
|
+
const footerInstance = render(React.createElement(Box, { flexDirection: "column", width: determineWidthToUse(columns, config.maxWidth) }, footerComponent({ columns, data: {}, key: 'footer' })), { stdout: footerOutput.stream });
|
|
349
|
+
footerInstance.unmount();
|
|
350
|
+
footerOutput.maybePrintLastFrame();
|
|
351
|
+
}
|
|
288
352
|
/**
|
|
289
353
|
* Renders a table with the given data.
|
|
290
354
|
* @param options see {@link TableOptions}
|
|
291
355
|
*/
|
|
292
356
|
export function printTable(options) {
|
|
357
|
+
if (options.data.length > 50_000) {
|
|
358
|
+
renderTableInChunks(options);
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
293
361
|
const output = new Output();
|
|
294
362
|
const instance = render(React.createElement(Table, { ...options }), { stdout: output.stream });
|
|
295
363
|
instance.unmount();
|
|
@@ -299,6 +367,9 @@ function Container(props) {
|
|
|
299
367
|
return (React.createElement(Box, { flexWrap: "wrap", flexDirection: props.direction ?? 'row', ...props }, props.children));
|
|
300
368
|
}
|
|
301
369
|
export function printTables(tables, options) {
|
|
370
|
+
if (tables.reduce((acc, table) => acc + table.data.length, 0) > 30_000) {
|
|
371
|
+
throw new Error('The data is too large to print multiple tables. Please use `printTable` instead.');
|
|
372
|
+
}
|
|
302
373
|
const output = new Output();
|
|
303
374
|
const leftMargin = options?.marginLeft ?? options?.margin ?? 0;
|
|
304
375
|
const rightMargin = options?.marginRight ?? options?.margin ?? 0;
|
package/lib/utils.js
CHANGED
|
@@ -67,14 +67,18 @@ export function getColumns(config, headings) {
|
|
|
67
67
|
};
|
|
68
68
|
});
|
|
69
69
|
const numberOfBorders = widths.length + 1;
|
|
70
|
-
const calculateTableWidth = (widths) => widths.map((w) => w.width).reduce((a, b) => a + b) + numberOfBorders;
|
|
70
|
+
const calculateTableWidth = (widths) => widths.map((w) => w.width + w.padding * 2).reduce((a, b) => a + b) + numberOfBorders;
|
|
71
71
|
// If the table is too wide, reduce the width of the largest column as little as possible to fit the table.
|
|
72
72
|
// At most, it will reduce the width to the length of the column's header plus padding.
|
|
73
73
|
// If the table is still too wide, it will reduce the width of the next largest column and so on
|
|
74
74
|
let tableWidth = calculateTableWidth(widths);
|
|
75
75
|
const seen = new Set();
|
|
76
76
|
while (tableWidth > maxWidth) {
|
|
77
|
-
const largestColumn = widths.
|
|
77
|
+
const largestColumn = widths.filter((w) => !seen.has(w.key)).sort((a, b) => b.width - a.width)[0];
|
|
78
|
+
if (!largestColumn)
|
|
79
|
+
break;
|
|
80
|
+
if (seen.has(largestColumn.key))
|
|
81
|
+
break;
|
|
78
82
|
const header = String(headings[largestColumn.key]).length;
|
|
79
83
|
// The minimum width of a column is the width of the header plus padding on both sides
|
|
80
84
|
const minWidth = header + largestColumn.padding * 2;
|
|
@@ -82,8 +86,6 @@ export function getColumns(config, headings) {
|
|
|
82
86
|
const newWidth = largestColumn.width - difference < minWidth ? minWidth : largestColumn.width - difference;
|
|
83
87
|
largestColumn.width = newWidth;
|
|
84
88
|
tableWidth = calculateTableWidth(widths);
|
|
85
|
-
if (seen.has(largestColumn.key))
|
|
86
|
-
break;
|
|
87
89
|
seen.add(largestColumn.key);
|
|
88
90
|
}
|
|
89
91
|
return widths;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oclif/table",
|
|
3
3
|
"description": "Display table in terminal",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.19",
|
|
5
5
|
"author": "Salesforce",
|
|
6
6
|
"bugs": "https://github.com/oclif/table/issues",
|
|
7
7
|
"dependencies": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"@oclif/prettier-config": "^0.2.1",
|
|
22
22
|
"@oclif/test": "^4.0.9",
|
|
23
23
|
"@types/chai": "^4.3.16",
|
|
24
|
-
"@types/mocha": "^10.0.
|
|
24
|
+
"@types/mocha": "^10.0.9",
|
|
25
25
|
"@types/node": "^18",
|
|
26
26
|
"@types/object-hash": "^3.0.6",
|
|
27
27
|
"@types/sinon": "^17.0.3",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"commitlint": "^19",
|
|
31
31
|
"eslint": "^8.57.0",
|
|
32
32
|
"eslint-config-oclif": "^5.2.0",
|
|
33
|
-
"eslint-config-oclif-typescript": "^3.1.
|
|
33
|
+
"eslint-config-oclif-typescript": "^3.1.12",
|
|
34
34
|
"eslint-config-prettier": "^9.1.0",
|
|
35
35
|
"eslint-config-xo": "^0.45.0",
|
|
36
36
|
"eslint-config-xo-react": "^0.27.0",
|