@oclif/table 0.4.8 → 0.4.10

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 CHANGED
@@ -8,7 +8,7 @@ import React from 'react';
8
8
  import stripAnsi from 'strip-ansi';
9
9
  import wrapAnsi from 'wrap-ansi';
10
10
  import { BORDER_SKELETONS } from './skeletons.js';
11
- import { allKeysInCollection, determineConfiguredWidth, determineWidthOfWrappedText, getColumns, getHeadings, intersperse, maybeStripAnsi, shouldUsePlainTable, sortData, } from './utils.js';
11
+ import { allKeysInCollection, determineConfiguredWidth, determineWidthOfWrappedText, getColumns, getColumnWidth, getHeadings, intersperse, maybeStripAnsi, shouldUsePlainTable, sortData, } from './utils.js';
12
12
  /**
13
13
  * Determine the width to use for the table.
14
14
  *
@@ -64,13 +64,15 @@ export function formatTextWithMargins({ horizontalAlignment, overflow, padding,
64
64
  // https://github.com/Shopify/cli/pull/995
65
65
  const valueWithNoZeroWidthChars = String(value).replaceAll('​', ' ');
66
66
  const spaceForText = width - padding * 2;
67
- if (stripAnsi(valueWithNoZeroWidthChars).length <= spaceForText) {
67
+ // Handle the simple case where text fits within the available space and doesn't contain any newlines.
68
+ if (stripAnsi(valueWithNoZeroWidthChars).length <= spaceForText && !valueWithNoZeroWidthChars.includes('\n')) {
68
69
  const spaces = width - stripAnsi(valueWithNoZeroWidthChars).length;
69
70
  return {
70
71
  text: valueWithNoZeroWidthChars,
71
72
  ...calculateMargins(spaces),
72
73
  };
73
74
  }
75
+ // Handle the case where the text needs to be wrapped.
74
76
  if (overflow === 'wrap') {
75
77
  const wrappedText = wrapAnsi(valueWithNoZeroWidthChars, spaceForText, {
76
78
  hard: true,
@@ -101,6 +103,7 @@ export function formatTextWithMargins({ horizontalAlignment, overflow, padding,
101
103
  text: lines.join('\n'),
102
104
  };
103
105
  }
106
+ // Handle the case where the text needs to be truncated.
104
107
  const text = cliTruncate(valueWithNoZeroWidthChars.replaceAll('\n', ' '), spaceForText, {
105
108
  position: determineTruncatePosition(overflow),
106
109
  });
@@ -286,7 +289,7 @@ const createStdout = () => {
286
289
  // https://github.com/vadimdemedes/ink/blob/v5.0.1/src/ink.tsx#L174
287
290
  // This might be a bad idea but it works.
288
291
  stdout.rows = 10_000;
289
- stdout.columns = process.stdout.columns ?? 80;
292
+ stdout.columns = getColumnWidth();
290
293
  const frames = [];
291
294
  stdout.write = (data) => {
292
295
  frames.push(data);
@@ -327,11 +330,12 @@ function renderPlainTable(props) {
327
330
  console.log(headerString);
328
331
  console.log('-'.repeat(headerString.length));
329
332
  for (const row of processedData) {
330
- const stringToPrint = columns.reduce((acc, column) => {
333
+ // Process all columns and get their formatted text
334
+ const columnTexts = columns.map((column) => {
331
335
  const { horizontalAlignment, overflow, padding, width } = column;
332
336
  const value = row[column.column];
333
337
  if (value === undefined || value === null) {
334
- return `${acc}${' '.repeat(width)}`;
338
+ return [' '.repeat(width)]; // Single line of spaces
335
339
  }
336
340
  const { marginLeft, marginRight, text } = formatTextWithMargins({
337
341
  horizontalAlignment,
@@ -340,9 +344,25 @@ function renderPlainTable(props) {
340
344
  value,
341
345
  width,
342
346
  });
343
- return `${acc}${' '.repeat(marginLeft)}${text}${' '.repeat(marginRight)}`;
344
- }, '');
345
- console.log(stringToPrint);
347
+ // Split the formatted text into lines
348
+ const lines = text.split('\n');
349
+ return lines.map((line) => `${' '.repeat(marginLeft)}${line.trimStart()}${' '.repeat(marginRight)}`);
350
+ });
351
+ // Find the maximum number of lines in any column
352
+ const maxLines = Math.max(...columnTexts.map((col) => col.length));
353
+ // Pad all columns to have the same number of lines
354
+ const paddedColumns = columnTexts.map((col, colIndex) => {
355
+ const column = columns[colIndex];
356
+ while (col.length < maxLines) {
357
+ col.push(' '.repeat(column.width)); // Add empty lines
358
+ }
359
+ return col;
360
+ });
361
+ // Print each line of the row
362
+ for (let lineIndex = 0; lineIndex < maxLines; lineIndex++) {
363
+ const lineToPrint = paddedColumns.map((col) => col[lineIndex]).join('');
364
+ console.log(lineToPrint);
365
+ }
346
366
  }
347
367
  console.log();
348
368
  }
@@ -396,7 +416,7 @@ export function printTables(tables, options) {
396
416
  const output = new Output();
397
417
  const leftMargin = options?.marginLeft ?? options?.margin ?? 0;
398
418
  const rightMargin = options?.marginRight ?? options?.margin ?? 0;
399
- const columns = process.stdout.columns - (leftMargin + rightMargin);
419
+ const columns = getColumnWidth() - (leftMargin + rightMargin);
400
420
  const processed = tables.map((table) => ({
401
421
  ...table,
402
422
  // adjust maxWidth to account for margin and columnGap
package/lib/utils.d.ts CHANGED
@@ -11,9 +11,22 @@ export declare function intersperse<T, I>(intersperser: (index: number) => I, el
11
11
  export declare function sortData<T extends Record<string, unknown>>(data: T[], sort?: Sort<T> | undefined): T[];
12
12
  export declare function allKeysInCollection<T extends Record<string, unknown>>(data: T[]): (keyof T)[];
13
13
  export declare function determineWidthOfWrappedText(text: string): number;
14
+ /**
15
+ * Gets the width of the terminal column.
16
+ * First checks for an override in the OCLIF_TABLE_COLUMN_OVERRIDE environment variable.
17
+ * If no override is set or the override is 0, returns the actual terminal width from process.stdout.columns.
18
+ *
19
+ * It's possible that `process.stdout.columns` is undefined or 0, which is okay. We'll end up using the table's natural width
20
+ * in that case. If that renders poorly for the user, they can set the OCLIF_TABLE_COLUMN_OVERRIDE environment variable to a
21
+ * non-zero value.
22
+ *
23
+ * @returns {number} The width of the terminal column
24
+ */
25
+ export declare function getColumnWidth(): number;
14
26
  /**
15
27
  * Determines the configured width based on the provided width value.
16
28
  * If no width is provided, it returns the width of the current terminal.
29
+ * - It's possible that `process.stdout.columns` is undefined, which is okay. We'll end up using the table's natural width.
17
30
  * If the provided width is a percentage, it calculates the width based on the percentage of the terminal width.
18
31
  * If the provided width is a number, it returns the provided width.
19
32
  * If the calculated width is greater than the terminal width, it returns the terminal width.
package/lib/utils.js CHANGED
@@ -42,9 +42,24 @@ export function determineWidthOfWrappedText(text) {
42
42
  const lines = text.split('\n');
43
43
  return lines.reduce((max, line) => Math.max(max, line.length), 0);
44
44
  }
45
+ /**
46
+ * Gets the width of the terminal column.
47
+ * First checks for an override in the OCLIF_TABLE_COLUMN_OVERRIDE environment variable.
48
+ * If no override is set or the override is 0, returns the actual terminal width from process.stdout.columns.
49
+ *
50
+ * It's possible that `process.stdout.columns` is undefined or 0, which is okay. We'll end up using the table's natural width
51
+ * in that case. If that renders poorly for the user, they can set the OCLIF_TABLE_COLUMN_OVERRIDE environment variable to a
52
+ * non-zero value.
53
+ *
54
+ * @returns {number} The width of the terminal column
55
+ */
56
+ export function getColumnWidth() {
57
+ return Number.parseInt(process.env.OCLIF_TABLE_COLUMN_OVERRIDE || '0', 10) || process.stdout.columns;
58
+ }
45
59
  /**
46
60
  * Determines the configured width based on the provided width value.
47
61
  * If no width is provided, it returns the width of the current terminal.
62
+ * - It's possible that `process.stdout.columns` is undefined, which is okay. We'll end up using the table's natural width.
48
63
  * If the provided width is a percentage, it calculates the width based on the percentage of the terminal width.
49
64
  * If the provided width is a number, it returns the provided width.
50
65
  * If the calculated width is greater than the terminal width, it returns the terminal width.
@@ -52,7 +67,7 @@ export function determineWidthOfWrappedText(text) {
52
67
  * @param providedWidth - The width value provided.
53
68
  * @returns The determined configured width.
54
69
  */
55
- export function determineConfiguredWidth(providedWidth, columns = process.stdout.columns) {
70
+ export function determineConfiguredWidth(providedWidth, columns = getColumnWidth()) {
56
71
  if (!providedWidth)
57
72
  return columns;
58
73
  const num = typeof providedWidth === 'string' && providedWidth.endsWith('%')
@@ -101,6 +116,10 @@ export function getColumns(config, headings) {
101
116
  let tableWidth = calculateTableWidth(widths);
102
117
  const seen = new Set();
103
118
  const reduceColumnWidths = (calcMinWidth) => {
119
+ // maxWidth === 0 is likely from a test environment where process.stdout.columns is undefined or 0
120
+ // In that case, we don't want to reduce the column widths and just use the table's natural width.
121
+ if (maxWidth === 0)
122
+ return;
104
123
  // If the table is too wide, reduce the width of the largest column as little as possible to fit the table.
105
124
  // If the table is still too wide, it will reduce the width of the next largest column and so on
106
125
  while (tableWidth > maxWidth) {
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.4.8",
4
+ "version": "0.4.10",
5
5
  "author": "Salesforce",
6
6
  "bugs": "https://github.com/oclif/table/issues",
7
7
  "dependencies": {
@@ -17,10 +17,10 @@
17
17
  },
18
18
  "devDependencies": {
19
19
  "@commitlint/config-conventional": "^19",
20
- "@eslint/compat": "^1.2.8",
20
+ "@eslint/compat": "^1.3.1",
21
21
  "@oclif/core": "^4",
22
22
  "@oclif/prettier-config": "^0.2.1",
23
- "@oclif/test": "^4.1.12",
23
+ "@oclif/test": "^4.1.13",
24
24
  "@types/chai": "^4.3.16",
25
25
  "@types/mocha": "^10.0.10",
26
26
  "@types/node": "^18",
@@ -29,10 +29,10 @@
29
29
  "ansis": "^3.17.0",
30
30
  "chai": "^4.5.0",
31
31
  "commitlint": "^19",
32
- "eslint": "^9.24.0",
33
- "eslint-config-oclif": "^6.0.42",
34
- "eslint-config-prettier": "^10.1.2",
35
- "eslint-config-xo": "^0.46.0",
32
+ "eslint": "^9.32.0",
33
+ "eslint-config-oclif": "^6.0.87",
34
+ "eslint-config-prettier": "^10.1.8",
35
+ "eslint-config-xo": "^0.48.0",
36
36
  "eslint-config-xo-react": "^0.28.0",
37
37
  "eslint-plugin-react": "^7.37.5",
38
38
  "eslint-plugin-react-hooks": "^4.6.2",
@@ -40,7 +40,7 @@
40
40
  "ink-testing-library": "^4.0.0",
41
41
  "lint-staged": "^15",
42
42
  "mocha": "^10.8.2",
43
- "prettier": "^3.5.3",
43
+ "prettier": "^3.6.2",
44
44
  "shx": "^0.4.0",
45
45
  "sinon": "^18",
46
46
  "terminal-link": "^3.0.0",