@oclif/table 0.2.3 → 0.3.0

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,66 @@ 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: column.column,
344
+ width,
345
+ });
346
+ const columnHeader = headings[text] ?? text;
347
+ return `${acc}${' '.repeat(marginLeft)}${columnHeader}${' '.repeat(marginRight)}`;
348
+ }, '');
349
+ console.log(headerString);
350
+ console.log('-'.repeat(headerString.length));
351
+ for (const row of processedData) {
352
+ const stringToPrint = columns.reduce((acc, column) => {
353
+ const { horizontalAlignment, overflow, padding, width } = column;
354
+ const value = row[column.column];
355
+ if (value === undefined || value === null) {
356
+ return `${acc}${' '.repeat(width)}`;
357
+ }
358
+ const { marginLeft, marginRight, text } = formatTextWithMargins({
359
+ horizontalAlignment,
360
+ overflow,
361
+ padding,
362
+ value,
363
+ width,
364
+ });
365
+ return `${acc}${' '.repeat(marginLeft)}${text}${' '.repeat(marginRight)}`;
366
+ }, '');
367
+ console.log(stringToPrint);
362
368
  }
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();
369
+ console.log();
367
370
  }
368
371
  /**
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.
372
+ * Prints a table based on the provided options. If the data length exceeds 10,000 entries,
373
+ * the table is rendered in a non-styled format to avoid memory issues.
371
374
  *
372
375
  * @template T - A generic type that extends a record with string keys and unknown values.
373
376
  * @param {TableOptions<T>} options - The options for rendering the table, including data and other configurations.
374
377
  * @returns {void}
375
378
  */
376
379
  export function printTable(options) {
377
- if (options.data.length > 50_000) {
378
- renderTableInChunks(options);
380
+ const limit = Number.parseInt(env.OCLIF_TABLE_LIMIT ?? env.SF_TABLE_LIMIT ?? '10000', 10) ?? 10_000;
381
+ if (options.data.length >= limit || shouldUsePlainTable()) {
382
+ renderPlainTable(options);
379
383
  return;
380
384
  }
381
385
  const output = new Output();
382
386
  const instance = render(React.createElement(Table, { ...options }), { stdout: output.stream });
383
387
  instance.unmount();
384
- output.maybePrintLastFrame();
388
+ output.printLastFrame();
385
389
  }
386
390
  /**
387
391
  * Generates a table as a string based on the provided options.
@@ -422,5 +426,5 @@ export function printTables(tables, options) {
422
426
  }));
423
427
  const instance = render(React.createElement(Container, { ...options }, processed.map((table) => (React.createElement(Table, { key: sha1(table), ...table })))), { stdout: output.stream });
424
428
  instance.unmount();
425
- output.maybePrintLastFrame();
429
+ output.printLastFrame();
426
430
  }
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,12 +1,12 @@
1
1
  {
2
2
  "name": "@oclif/table",
3
3
  "description": "Display table in terminal",
4
- "version": "0.2.3",
4
+ "version": "0.3.0",
5
5
  "author": "Salesforce",
6
6
  "bugs": "https://github.com/oclif/table/issues",
7
7
  "dependencies": {
8
8
  "@oclif/core": "^4",
9
- "@types/react": "^18.3.11",
9
+ "@types/react": "^18.3.12",
10
10
  "change-case": "^5.4.4",
11
11
  "cli-truncate": "^4.0.0",
12
12
  "ink": "^5.0.1",
@@ -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",