@oclif/table 0.2.4 → 0.3.1

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.d.ts CHANGED
@@ -27,8 +27,8 @@ export declare function Skeleton(props: React.PropsWithChildren & {
27
27
  readonly height?: number;
28
28
  }): React.JSX.Element;
29
29
  /**
30
- * Prints a table based on the provided options. If the data length exceeds 50,000 entries,
31
- * the table is rendered in chunks to handle large datasets efficiently.
30
+ * Prints a table based on the provided options. If the data length exceeds 10,000 entries,
31
+ * the table is rendered in a non-styled format to avoid memory issues.
32
32
  *
33
33
  * @template T - A generic type that extends a record with string keys and unknown values.
34
34
  * @param {TableOptions<T>} options - The options for rendering the table, including data and other configurations.
package/lib/table.js CHANGED
@@ -2,12 +2,13 @@
2
2
  import cliTruncate from 'cli-truncate';
3
3
  import { Box, Text, render } from 'ink';
4
4
  import { EventEmitter } from 'node:events';
5
+ import { env } from 'node:process';
5
6
  import { sha1 } from 'object-hash';
6
7
  import React from 'react';
7
8
  import stripAnsi from 'strip-ansi';
8
9
  import wrapAnsi from 'wrap-ansi';
9
10
  import { BORDER_SKELETONS } from './skeletons.js';
10
- import { allKeysInCollection, determineWidthOfWrappedText, getColumns, getHeadings, intersperse, maybeStripAnsi, sortData, } from './utils.js';
11
+ import { allKeysInCollection, determineWidthOfWrappedText, getColumns, getHeadings, intersperse, maybeStripAnsi, shouldUsePlainTable, sortData, } from './utils.js';
11
12
  /**
12
13
  * Determines the configured width based on the provided width value.
13
14
  * If no width is provided, it returns the width of the current terminal.
@@ -325,63 +326,65 @@ class Output {
325
326
  constructor() {
326
327
  this.stream = createStdout();
327
328
  }
328
- maybePrintLastFrame() {
329
+ printLastFrame() {
329
330
  process.stdout.write(`${this.stream.lastFrame()}\n`);
330
331
  }
331
332
  }
332
- function chunk(array, size) {
333
- return array.reduce((acc, _, i) => {
334
- if (i % size === 0)
335
- acc.push(array.slice(i, i + size));
336
- return acc;
337
- }, []);
338
- }
339
- function renderTableInChunks(props) {
340
- const { columns, config, dataComponent, footerComponent, headerComponent, headerFooterComponent, headingComponent, headings, processedData, separatorComponent, title, titleOptions, } = setup(props);
341
- const headerOutput = new Output();
342
- const headerInstance = render(React.createElement(Box, { flexDirection: "column", width: determineWidthToUse(columns, config.maxWidth) },
343
- title && React.createElement(Text, { ...titleOptions }, title),
344
- headerComponent({ columns, data: {}, key: 'header' }),
345
- headingComponent({ columns, data: headings, key: 'heading' }),
346
- headerFooterComponent({ columns, data: {}, key: 'footer' })), { stdout: headerOutput.stream });
347
- headerInstance.unmount();
348
- headerOutput.maybePrintLastFrame();
349
- const chunks = chunk(processedData, Math.max(1, Math.floor(process.stdout.rows / 2)));
350
- for (const chunk of chunks) {
351
- const chunkOutput = new Output();
352
- const instance = render(React.createElement(Box, { flexDirection: "column", width: determineWidthToUse(columns, config.maxWidth) }, chunk.map((row, index) => {
353
- // Calculate the hash of the row based on its value and position
354
- const key = `row-${sha1(row)}-${index}`;
355
- // Construct a row.
356
- return (React.createElement(Box, { key: key, flexDirection: "column" },
357
- separatorComponent({ columns, data: {}, key: `separator-${key}` }),
358
- dataComponent({ columns, data: row, key: `data-${key}` })));
359
- })), { stdout: chunkOutput.stream });
360
- instance.unmount();
361
- chunkOutput.maybePrintLastFrame();
333
+ function renderPlainTable(props) {
334
+ const { columns, headings, processedData, title } = setup(props);
335
+ if (title)
336
+ console.log(title);
337
+ const headerString = columns.reduce((acc, column) => {
338
+ const { horizontalAlignment, overflow, padding, width } = column;
339
+ const { marginLeft, marginRight, text } = formatTextWithMargins({
340
+ horizontalAlignment,
341
+ overflow,
342
+ padding,
343
+ value: headings[column.column] ?? column.column,
344
+ width,
345
+ });
346
+ return `${acc}${' '.repeat(marginLeft)}${text}${' '.repeat(marginRight)}`;
347
+ }, '');
348
+ console.log(headerString);
349
+ console.log('-'.repeat(headerString.length));
350
+ for (const row of processedData) {
351
+ const stringToPrint = columns.reduce((acc, column) => {
352
+ const { horizontalAlignment, overflow, padding, width } = column;
353
+ const value = row[column.column];
354
+ if (value === undefined || value === null) {
355
+ return `${acc}${' '.repeat(width)}`;
356
+ }
357
+ const { marginLeft, marginRight, text } = formatTextWithMargins({
358
+ horizontalAlignment,
359
+ overflow,
360
+ padding,
361
+ value,
362
+ width,
363
+ });
364
+ return `${acc}${' '.repeat(marginLeft)}${text}${' '.repeat(marginRight)}`;
365
+ }, '');
366
+ console.log(stringToPrint);
362
367
  }
363
- const footerOutput = new Output();
364
- const footerInstance = render(React.createElement(Box, { flexDirection: "column", width: determineWidthToUse(columns, config.maxWidth) }, footerComponent({ columns, data: {}, key: 'footer' })), { stdout: footerOutput.stream });
365
- footerInstance.unmount();
366
- footerOutput.maybePrintLastFrame();
368
+ console.log();
367
369
  }
368
370
  /**
369
- * Prints a table based on the provided options. If the data length exceeds 50,000 entries,
370
- * the table is rendered in chunks to handle large datasets efficiently.
371
+ * Prints a table based on the provided options. If the data length exceeds 10,000 entries,
372
+ * the table is rendered in a non-styled format to avoid memory issues.
371
373
  *
372
374
  * @template T - A generic type that extends a record with string keys and unknown values.
373
375
  * @param {TableOptions<T>} options - The options for rendering the table, including data and other configurations.
374
376
  * @returns {void}
375
377
  */
376
378
  export function printTable(options) {
377
- if (options.data.length > 50_000) {
378
- renderTableInChunks(options);
379
+ const limit = Number.parseInt(env.OCLIF_TABLE_LIMIT ?? env.SF_TABLE_LIMIT ?? '10000', 10) ?? 10_000;
380
+ if (options.data.length >= limit || shouldUsePlainTable()) {
381
+ renderPlainTable(options);
379
382
  return;
380
383
  }
381
384
  const output = new Output();
382
385
  const instance = render(React.createElement(Table, { ...options }), { stdout: output.stream });
383
386
  instance.unmount();
384
- output.maybePrintLastFrame();
387
+ output.printLastFrame();
385
388
  }
386
389
  /**
387
390
  * Generates a table as a string based on the provided options.
@@ -408,7 +411,7 @@ function Container(props) {
408
411
  * @throws {Error} Throws an error if the total number of rows across all tables exceeds 30,000.
409
412
  */
410
413
  export function printTables(tables, options) {
411
- if (tables.reduce((acc, table) => acc + table.data.length, 0) > 30_000) {
414
+ if (tables.reduce((acc, table) => acc + table.data.length, 0) > 10_000) {
412
415
  throw new Error('The data is too large to print multiple tables. Please use `printTable` instead.');
413
416
  }
414
417
  const output = new Output();
@@ -422,5 +425,5 @@ export function printTables(tables, options) {
422
425
  }));
423
426
  const instance = render(React.createElement(Container, { ...options }, processed.map((table) => (React.createElement(Table, { key: sha1(table), ...table })))), { stdout: output.stream });
424
427
  instance.unmount();
425
- output.maybePrintLastFrame();
428
+ output.printLastFrame();
426
429
  }
package/lib/utils.d.ts CHANGED
@@ -14,3 +14,13 @@ export declare function determineWidthOfWrappedText(text: string): number;
14
14
  export declare function getColumns<T extends Record<string, unknown>>(config: Config<T>, headings: Partial<T>): Column<T>[];
15
15
  export declare function getHeadings<T extends Record<string, unknown>>(config: Config<T>): Partial<T>;
16
16
  export declare function maybeStripAnsi<T extends Record<string, unknown>[]>(data: T, noStyle: boolean): T;
17
+ /**
18
+ * Determines whether the plain text table should be used.
19
+ *
20
+ * If the OCLIF_TABLE_SKIP_CI_CHECK environment variable is set to a truthy value, the CI check will be skipped.
21
+ *
22
+ * If the CI environment variable is set, the plain text table will be used.
23
+ *
24
+ * @returns {boolean} True if the plain text table should be used, false otherwise.
25
+ */
26
+ export declare function shouldUsePlainTable(): boolean;
package/lib/utils.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { camelCase, capitalCase, constantCase, kebabCase, pascalCase, sentenceCase, snakeCase } from 'change-case';
2
2
  import { orderBy } from 'natural-orderby';
3
+ import { env } from 'node:process';
3
4
  import stripAnsi from 'strip-ansi';
4
5
  /**
5
6
  * Intersperses a list of elements with another element.
@@ -144,3 +145,24 @@ export function maybeStripAnsi(data, noStyle) {
144
145
  }
145
146
  return newData;
146
147
  }
148
+ function isTruthy(value) {
149
+ return value !== '0' && value !== 'false';
150
+ }
151
+ /**
152
+ * Determines whether the plain text table should be used.
153
+ *
154
+ * If the OCLIF_TABLE_SKIP_CI_CHECK environment variable is set to a truthy value, the CI check will be skipped.
155
+ *
156
+ * If the CI environment variable is set, the plain text table will be used.
157
+ *
158
+ * @returns {boolean} True if the plain text table should be used, false otherwise.
159
+ */
160
+ export function shouldUsePlainTable() {
161
+ if (env.OCLIF_TABLE_SKIP_CI_CHECK && isTruthy(env.OCLIF_TABLE_SKIP_CI_CHECK))
162
+ return false;
163
+ // Inspired by https://github.com/sindresorhus/is-in-ci
164
+ if (isTruthy(env.CI) &&
165
+ ('CI' in env || 'CONTINUOUS_INTEGRATION' in env || Object.keys(env).some((key) => key.startsWith('CI_'))))
166
+ return true;
167
+ return false;
168
+ }
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.2.4",
4
+ "version": "0.3.1",
5
5
  "author": "Salesforce",
6
6
  "bugs": "https://github.com/oclif/table/issues",
7
7
  "dependencies": {
@@ -34,7 +34,7 @@
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",
37
- "eslint-plugin-react": "^7.37.1",
37
+ "eslint-plugin-react": "^7.37.2",
38
38
  "eslint-plugin-react-hooks": "^4.6.2",
39
39
  "husky": "^9.1.6",
40
40
  "ink-testing-library": "^4.0.0",