You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

127 lines
2.9 KiB

4 years ago
  1. import _ from 'lodash';
  2. import makeStreamConfig from './makeStreamConfig';
  3. import drawRow from './drawRow';
  4. import {
  5. drawBorderBottom,
  6. drawBorderJoin,
  7. drawBorderTop
  8. } from './drawBorder';
  9. import stringifyTableData from './stringifyTableData';
  10. import truncateTableData from './truncateTableData';
  11. import mapDataUsingRowHeightIndex from './mapDataUsingRowHeightIndex';
  12. import alignTableData from './alignTableData';
  13. import padTableData from './padTableData';
  14. import calculateRowHeightIndex from './calculateRowHeightIndex';
  15. /**
  16. * @param {Array} data
  17. * @param {Object} config
  18. * @returns {Array}
  19. */
  20. const prepareData = (data, config) => {
  21. let rows;
  22. rows = stringifyTableData(data);
  23. rows = truncateTableData(data, config);
  24. const rowHeightIndex = calculateRowHeightIndex(rows, config);
  25. rows = mapDataUsingRowHeightIndex(rows, rowHeightIndex, config);
  26. rows = alignTableData(rows, config);
  27. rows = padTableData(rows, config);
  28. return rows;
  29. };
  30. /**
  31. * @param {string[]} row
  32. * @param {number[]} columnWidthIndex
  33. * @param {Object} config
  34. * @returns {undefined}
  35. */
  36. const create = (row, columnWidthIndex, config) => {
  37. const rows = prepareData([row], config);
  38. const body = rows.map((literalRow) => {
  39. return drawRow(literalRow, config.border);
  40. }).join('');
  41. let output;
  42. output = '';
  43. output += drawBorderTop(columnWidthIndex, config.border);
  44. output += body;
  45. output += drawBorderBottom(columnWidthIndex, config.border);
  46. output = _.trimEnd(output);
  47. process.stdout.write(output);
  48. };
  49. /**
  50. * @param {string[]} row
  51. * @param {number[]} columnWidthIndex
  52. * @param {Object} config
  53. * @returns {undefined}
  54. */
  55. const append = (row, columnWidthIndex, config) => {
  56. const rows = prepareData([row], config);
  57. const body = rows.map((literalRow) => {
  58. return drawRow(literalRow, config.border);
  59. }).join('');
  60. let output = '';
  61. const bottom = drawBorderBottom(columnWidthIndex, config.border);
  62. if (bottom !== '\n') {
  63. output = '\r\u001B[K';
  64. }
  65. output += drawBorderJoin(columnWidthIndex, config.border);
  66. output += body;
  67. output += bottom;
  68. output = _.trimEnd(output);
  69. process.stdout.write(output);
  70. };
  71. /**
  72. * @param {Object} userConfig
  73. * @returns {Object}
  74. */
  75. export default (userConfig = {}) => {
  76. const config = makeStreamConfig(userConfig);
  77. // @todo Use 'Object.values' when Node.js v6 support is dropped.
  78. const columnWidthIndex = _.values(_.mapValues(config.columns, (column) => {
  79. return column.width + column.paddingLeft + column.paddingRight;
  80. }));
  81. let empty;
  82. empty = true;
  83. return {
  84. /**
  85. * @param {string[]} row
  86. * @returns {undefined}
  87. */
  88. write: (row) => {
  89. if (row.length !== config.columnCount) {
  90. throw new Error('Row cell count does not match the config.columnCount.');
  91. }
  92. if (empty) {
  93. empty = false;
  94. return create(row, columnWidthIndex, config);
  95. } else {
  96. return append(row, columnWidthIndex, config);
  97. }
  98. }
  99. };
  100. };