11cb0ef41Sopenharmony_ci"use strict"
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ciconst wcwidth = require('./width')
41cb0ef41Sopenharmony_ciconst {
51cb0ef41Sopenharmony_ci  padRight,
61cb0ef41Sopenharmony_ci  padCenter,
71cb0ef41Sopenharmony_ci  padLeft,
81cb0ef41Sopenharmony_ci  splitIntoLines,
91cb0ef41Sopenharmony_ci  splitLongWords,
101cb0ef41Sopenharmony_ci  truncateString
111cb0ef41Sopenharmony_ci} = require('./utils')
121cb0ef41Sopenharmony_ci
131cb0ef41Sopenharmony_ciconst DEFAULT_HEADING_TRANSFORM = key => key.toUpperCase()
141cb0ef41Sopenharmony_ci
151cb0ef41Sopenharmony_ciconst DEFAULT_DATA_TRANSFORM = (cell, column, index) => cell
161cb0ef41Sopenharmony_ci
171cb0ef41Sopenharmony_ciconst DEFAULTS = Object.freeze({
181cb0ef41Sopenharmony_ci  maxWidth: Infinity,
191cb0ef41Sopenharmony_ci  minWidth: 0,
201cb0ef41Sopenharmony_ci  columnSplitter: ' ',
211cb0ef41Sopenharmony_ci  truncate: false,
221cb0ef41Sopenharmony_ci  truncateMarker: '…',
231cb0ef41Sopenharmony_ci  preserveNewLines: false,
241cb0ef41Sopenharmony_ci  paddingChr: ' ',
251cb0ef41Sopenharmony_ci  showHeaders: true,
261cb0ef41Sopenharmony_ci  headingTransform: DEFAULT_HEADING_TRANSFORM,
271cb0ef41Sopenharmony_ci  dataTransform: DEFAULT_DATA_TRANSFORM
281cb0ef41Sopenharmony_ci})
291cb0ef41Sopenharmony_ci
301cb0ef41Sopenharmony_cimodule.exports = function(items, options = {}) {
311cb0ef41Sopenharmony_ci
321cb0ef41Sopenharmony_ci  let columnConfigs = options.config || {}
331cb0ef41Sopenharmony_ci  delete options.config // remove config so doesn't appear on every column.
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_ci  let maxLineWidth = options.maxLineWidth || Infinity
361cb0ef41Sopenharmony_ci  if (maxLineWidth === 'auto') maxLineWidth = process.stdout.columns || Infinity
371cb0ef41Sopenharmony_ci  delete options.maxLineWidth // this is a line control option, don't pass it to column
381cb0ef41Sopenharmony_ci
391cb0ef41Sopenharmony_ci  // Option defaults inheritance:
401cb0ef41Sopenharmony_ci  // options.config[columnName] => options => DEFAULTS
411cb0ef41Sopenharmony_ci  options = mixin({}, DEFAULTS, options)
421cb0ef41Sopenharmony_ci
431cb0ef41Sopenharmony_ci  options.config = options.config || Object.create(null)
441cb0ef41Sopenharmony_ci
451cb0ef41Sopenharmony_ci  options.spacing = options.spacing || '\n' // probably useless
461cb0ef41Sopenharmony_ci  options.preserveNewLines = !!options.preserveNewLines
471cb0ef41Sopenharmony_ci  options.showHeaders = !!options.showHeaders;
481cb0ef41Sopenharmony_ci  options.columns = options.columns || options.include // alias include/columns, prefer columns if supplied
491cb0ef41Sopenharmony_ci  let columnNames = options.columns || [] // optional user-supplied columns to include
501cb0ef41Sopenharmony_ci
511cb0ef41Sopenharmony_ci  items = toArray(items, columnNames)
521cb0ef41Sopenharmony_ci
531cb0ef41Sopenharmony_ci  // if not suppled column names, automatically determine columns from data keys
541cb0ef41Sopenharmony_ci  if (!columnNames.length) {
551cb0ef41Sopenharmony_ci    items.forEach(function(item) {
561cb0ef41Sopenharmony_ci      for (let columnName in item) {
571cb0ef41Sopenharmony_ci        if (columnNames.indexOf(columnName) === -1) columnNames.push(columnName)
581cb0ef41Sopenharmony_ci      }
591cb0ef41Sopenharmony_ci    })
601cb0ef41Sopenharmony_ci  }
611cb0ef41Sopenharmony_ci
621cb0ef41Sopenharmony_ci  // initialize column defaults (each column inherits from options.config)
631cb0ef41Sopenharmony_ci  let columns = columnNames.reduce((columns, columnName) => {
641cb0ef41Sopenharmony_ci    let column = Object.create(options)
651cb0ef41Sopenharmony_ci    columns[columnName] = mixin(column, columnConfigs[columnName])
661cb0ef41Sopenharmony_ci    return columns
671cb0ef41Sopenharmony_ci  }, Object.create(null))
681cb0ef41Sopenharmony_ci
691cb0ef41Sopenharmony_ci  // sanitize column settings
701cb0ef41Sopenharmony_ci  columnNames.forEach(columnName => {
711cb0ef41Sopenharmony_ci    let column = columns[columnName]
721cb0ef41Sopenharmony_ci    column.name = columnName
731cb0ef41Sopenharmony_ci    column.maxWidth = Math.ceil(column.maxWidth)
741cb0ef41Sopenharmony_ci    column.minWidth = Math.ceil(column.minWidth)
751cb0ef41Sopenharmony_ci    column.truncate = !!column.truncate
761cb0ef41Sopenharmony_ci    column.align = column.align || 'left'
771cb0ef41Sopenharmony_ci  })
781cb0ef41Sopenharmony_ci
791cb0ef41Sopenharmony_ci  // sanitize data
801cb0ef41Sopenharmony_ci  items = items.map(item => {
811cb0ef41Sopenharmony_ci    let result = Object.create(null)
821cb0ef41Sopenharmony_ci    columnNames.forEach(columnName => {
831cb0ef41Sopenharmony_ci      // null/undefined -> ''
841cb0ef41Sopenharmony_ci      result[columnName] = item[columnName] != null ? item[columnName] : ''
851cb0ef41Sopenharmony_ci      // toString everything
861cb0ef41Sopenharmony_ci      result[columnName] = '' + result[columnName]
871cb0ef41Sopenharmony_ci      if (columns[columnName].preserveNewLines) {
881cb0ef41Sopenharmony_ci        // merge non-newline whitespace chars
891cb0ef41Sopenharmony_ci        result[columnName] = result[columnName].replace(/[^\S\n]/gmi, ' ')
901cb0ef41Sopenharmony_ci      } else {
911cb0ef41Sopenharmony_ci        // merge all whitespace chars
921cb0ef41Sopenharmony_ci        result[columnName] = result[columnName].replace(/\s/gmi, ' ')
931cb0ef41Sopenharmony_ci      }
941cb0ef41Sopenharmony_ci    })
951cb0ef41Sopenharmony_ci    return result
961cb0ef41Sopenharmony_ci  })
971cb0ef41Sopenharmony_ci
981cb0ef41Sopenharmony_ci  // transform data cells
991cb0ef41Sopenharmony_ci  columnNames.forEach(columnName => {
1001cb0ef41Sopenharmony_ci    let column = columns[columnName]
1011cb0ef41Sopenharmony_ci    items = items.map((item, index) => {
1021cb0ef41Sopenharmony_ci      let col = Object.create(column)
1031cb0ef41Sopenharmony_ci      item[columnName] = column.dataTransform(item[columnName], col, index)
1041cb0ef41Sopenharmony_ci
1051cb0ef41Sopenharmony_ci      let changedKeys = Object.keys(col)
1061cb0ef41Sopenharmony_ci      // disable default heading transform if we wrote to column.name
1071cb0ef41Sopenharmony_ci      if (changedKeys.indexOf('name') !== -1) {
1081cb0ef41Sopenharmony_ci        if (column.headingTransform !== DEFAULT_HEADING_TRANSFORM) return
1091cb0ef41Sopenharmony_ci        column.headingTransform = heading => heading
1101cb0ef41Sopenharmony_ci      }
1111cb0ef41Sopenharmony_ci      changedKeys.forEach(key => column[key] = col[key])
1121cb0ef41Sopenharmony_ci      return item
1131cb0ef41Sopenharmony_ci    })
1141cb0ef41Sopenharmony_ci  })
1151cb0ef41Sopenharmony_ci
1161cb0ef41Sopenharmony_ci  // add headers
1171cb0ef41Sopenharmony_ci  let headers = {}
1181cb0ef41Sopenharmony_ci  if(options.showHeaders) {
1191cb0ef41Sopenharmony_ci    columnNames.forEach(columnName => {
1201cb0ef41Sopenharmony_ci      let column = columns[columnName]
1211cb0ef41Sopenharmony_ci
1221cb0ef41Sopenharmony_ci      if(!column.showHeaders){
1231cb0ef41Sopenharmony_ci        headers[columnName] = '';
1241cb0ef41Sopenharmony_ci        return;
1251cb0ef41Sopenharmony_ci      }
1261cb0ef41Sopenharmony_ci
1271cb0ef41Sopenharmony_ci      headers[columnName] = column.headingTransform(column.name)
1281cb0ef41Sopenharmony_ci    })
1291cb0ef41Sopenharmony_ci    items.unshift(headers)
1301cb0ef41Sopenharmony_ci  }
1311cb0ef41Sopenharmony_ci  // get actual max-width between min & max
1321cb0ef41Sopenharmony_ci  // based on length of data in columns
1331cb0ef41Sopenharmony_ci  columnNames.forEach(columnName => {
1341cb0ef41Sopenharmony_ci    let column = columns[columnName]
1351cb0ef41Sopenharmony_ci    column.width = items
1361cb0ef41Sopenharmony_ci    .map(item => item[columnName])
1371cb0ef41Sopenharmony_ci    .reduce((min, cur) => {
1381cb0ef41Sopenharmony_ci      // if already at maxWidth don't bother testing
1391cb0ef41Sopenharmony_ci      if (min >= column.maxWidth) return min
1401cb0ef41Sopenharmony_ci      return Math.max(min, Math.min(column.maxWidth, Math.max(column.minWidth, wcwidth(cur))))
1411cb0ef41Sopenharmony_ci    }, 0)
1421cb0ef41Sopenharmony_ci  })
1431cb0ef41Sopenharmony_ci
1441cb0ef41Sopenharmony_ci  // split long words so they can break onto multiple lines
1451cb0ef41Sopenharmony_ci  columnNames.forEach(columnName => {
1461cb0ef41Sopenharmony_ci    let column = columns[columnName]
1471cb0ef41Sopenharmony_ci    items = items.map(item => {
1481cb0ef41Sopenharmony_ci      item[columnName] = splitLongWords(item[columnName], column.width, column.truncateMarker)
1491cb0ef41Sopenharmony_ci      return item
1501cb0ef41Sopenharmony_ci    })
1511cb0ef41Sopenharmony_ci  })
1521cb0ef41Sopenharmony_ci
1531cb0ef41Sopenharmony_ci  // wrap long lines. each item is now an array of lines.
1541cb0ef41Sopenharmony_ci  columnNames.forEach(columnName => {
1551cb0ef41Sopenharmony_ci    let column = columns[columnName]
1561cb0ef41Sopenharmony_ci    items = items.map((item, index) => {
1571cb0ef41Sopenharmony_ci      let cell = item[columnName]
1581cb0ef41Sopenharmony_ci      item[columnName] = splitIntoLines(cell, column.width)
1591cb0ef41Sopenharmony_ci
1601cb0ef41Sopenharmony_ci      // if truncating required, only include first line + add truncation char
1611cb0ef41Sopenharmony_ci      if (column.truncate && item[columnName].length > 1) {
1621cb0ef41Sopenharmony_ci        item[columnName] = splitIntoLines(cell, column.width - wcwidth(column.truncateMarker))
1631cb0ef41Sopenharmony_ci        let firstLine = item[columnName][0]
1641cb0ef41Sopenharmony_ci        if (!endsWith(firstLine, column.truncateMarker)) item[columnName][0] += column.truncateMarker
1651cb0ef41Sopenharmony_ci        item[columnName] = item[columnName].slice(0, 1)
1661cb0ef41Sopenharmony_ci      }
1671cb0ef41Sopenharmony_ci      return item
1681cb0ef41Sopenharmony_ci    })
1691cb0ef41Sopenharmony_ci  })
1701cb0ef41Sopenharmony_ci
1711cb0ef41Sopenharmony_ci  // recalculate column widths from truncated output/lines
1721cb0ef41Sopenharmony_ci  columnNames.forEach(columnName => {
1731cb0ef41Sopenharmony_ci    let column = columns[columnName]
1741cb0ef41Sopenharmony_ci    column.width = items.map(item => {
1751cb0ef41Sopenharmony_ci      return item[columnName].reduce((min, cur) => {
1761cb0ef41Sopenharmony_ci        if (min >= column.maxWidth) return min
1771cb0ef41Sopenharmony_ci        return Math.max(min, Math.min(column.maxWidth, Math.max(column.minWidth, wcwidth(cur))))
1781cb0ef41Sopenharmony_ci      }, 0)
1791cb0ef41Sopenharmony_ci    }).reduce((min, cur) => {
1801cb0ef41Sopenharmony_ci      if (min >= column.maxWidth) return min
1811cb0ef41Sopenharmony_ci      return Math.max(min, Math.min(column.maxWidth, Math.max(column.minWidth, cur)))
1821cb0ef41Sopenharmony_ci    }, 0)
1831cb0ef41Sopenharmony_ci  })
1841cb0ef41Sopenharmony_ci
1851cb0ef41Sopenharmony_ci
1861cb0ef41Sopenharmony_ci  let rows = createRows(items, columns, columnNames, options.paddingChr) // merge lines into rows
1871cb0ef41Sopenharmony_ci  // conceive output
1881cb0ef41Sopenharmony_ci  return rows.reduce((output, row) => {
1891cb0ef41Sopenharmony_ci    return output.concat(row.reduce((rowOut, line) => {
1901cb0ef41Sopenharmony_ci      return rowOut.concat(line.join(options.columnSplitter))
1911cb0ef41Sopenharmony_ci    }, []))
1921cb0ef41Sopenharmony_ci  }, [])
1931cb0ef41Sopenharmony_ci  .map(line => truncateString(line, maxLineWidth))
1941cb0ef41Sopenharmony_ci  .join(options.spacing)
1951cb0ef41Sopenharmony_ci}
1961cb0ef41Sopenharmony_ci
1971cb0ef41Sopenharmony_ci/**
1981cb0ef41Sopenharmony_ci * Convert wrapped lines into rows with padded values.
1991cb0ef41Sopenharmony_ci *
2001cb0ef41Sopenharmony_ci * @param Array items data to process
2011cb0ef41Sopenharmony_ci * @param Array columns column width settings for wrapping
2021cb0ef41Sopenharmony_ci * @param Array columnNames column ordering
2031cb0ef41Sopenharmony_ci * @return Array items wrapped in arrays, corresponding to lines
2041cb0ef41Sopenharmony_ci */
2051cb0ef41Sopenharmony_ci
2061cb0ef41Sopenharmony_cifunction createRows(items, columns, columnNames, paddingChr) {
2071cb0ef41Sopenharmony_ci  return items.map(item => {
2081cb0ef41Sopenharmony_ci    let row = []
2091cb0ef41Sopenharmony_ci    let numLines = 0
2101cb0ef41Sopenharmony_ci    columnNames.forEach(columnName => {
2111cb0ef41Sopenharmony_ci      numLines = Math.max(numLines, item[columnName].length)
2121cb0ef41Sopenharmony_ci    })
2131cb0ef41Sopenharmony_ci    // combine matching lines of each rows
2141cb0ef41Sopenharmony_ci    for (let i = 0; i < numLines; i++) {
2151cb0ef41Sopenharmony_ci      row[i] = row[i] || []
2161cb0ef41Sopenharmony_ci      columnNames.forEach(columnName => {
2171cb0ef41Sopenharmony_ci        let column = columns[columnName]
2181cb0ef41Sopenharmony_ci        let val = item[columnName][i] || '' // || '' ensures empty columns get padded
2191cb0ef41Sopenharmony_ci        if (column.align === 'right') row[i].push(padLeft(val, column.width, paddingChr))
2201cb0ef41Sopenharmony_ci        else if (column.align === 'center' || column.align === 'centre') row[i].push(padCenter(val, column.width, paddingChr))
2211cb0ef41Sopenharmony_ci        else row[i].push(padRight(val, column.width, paddingChr))
2221cb0ef41Sopenharmony_ci      })
2231cb0ef41Sopenharmony_ci    }
2241cb0ef41Sopenharmony_ci    return row
2251cb0ef41Sopenharmony_ci  })
2261cb0ef41Sopenharmony_ci}
2271cb0ef41Sopenharmony_ci
2281cb0ef41Sopenharmony_ci/**
2291cb0ef41Sopenharmony_ci * Object.assign
2301cb0ef41Sopenharmony_ci *
2311cb0ef41Sopenharmony_ci * @return Object Object with properties mixed in.
2321cb0ef41Sopenharmony_ci */
2331cb0ef41Sopenharmony_ci
2341cb0ef41Sopenharmony_cifunction mixin(...args) {
2351cb0ef41Sopenharmony_ci  if (Object.assign) return Object.assign(...args)
2361cb0ef41Sopenharmony_ci  return ObjectAssign(...args)
2371cb0ef41Sopenharmony_ci}
2381cb0ef41Sopenharmony_ci
2391cb0ef41Sopenharmony_cifunction ObjectAssign(target, firstSource) {
2401cb0ef41Sopenharmony_ci  "use strict";
2411cb0ef41Sopenharmony_ci  if (target === undefined || target === null)
2421cb0ef41Sopenharmony_ci    throw new TypeError("Cannot convert first argument to object");
2431cb0ef41Sopenharmony_ci
2441cb0ef41Sopenharmony_ci  var to = Object(target);
2451cb0ef41Sopenharmony_ci
2461cb0ef41Sopenharmony_ci  var hasPendingException = false;
2471cb0ef41Sopenharmony_ci  var pendingException;
2481cb0ef41Sopenharmony_ci
2491cb0ef41Sopenharmony_ci  for (var i = 1; i < arguments.length; i++) {
2501cb0ef41Sopenharmony_ci    var nextSource = arguments[i];
2511cb0ef41Sopenharmony_ci    if (nextSource === undefined || nextSource === null)
2521cb0ef41Sopenharmony_ci      continue;
2531cb0ef41Sopenharmony_ci
2541cb0ef41Sopenharmony_ci    var keysArray = Object.keys(Object(nextSource));
2551cb0ef41Sopenharmony_ci    for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
2561cb0ef41Sopenharmony_ci      var nextKey = keysArray[nextIndex];
2571cb0ef41Sopenharmony_ci      try {
2581cb0ef41Sopenharmony_ci        var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
2591cb0ef41Sopenharmony_ci        if (desc !== undefined && desc.enumerable)
2601cb0ef41Sopenharmony_ci          to[nextKey] = nextSource[nextKey];
2611cb0ef41Sopenharmony_ci      } catch (e) {
2621cb0ef41Sopenharmony_ci        if (!hasPendingException) {
2631cb0ef41Sopenharmony_ci          hasPendingException = true;
2641cb0ef41Sopenharmony_ci          pendingException = e;
2651cb0ef41Sopenharmony_ci        }
2661cb0ef41Sopenharmony_ci      }
2671cb0ef41Sopenharmony_ci    }
2681cb0ef41Sopenharmony_ci
2691cb0ef41Sopenharmony_ci    if (hasPendingException)
2701cb0ef41Sopenharmony_ci      throw pendingException;
2711cb0ef41Sopenharmony_ci  }
2721cb0ef41Sopenharmony_ci  return to;
2731cb0ef41Sopenharmony_ci}
2741cb0ef41Sopenharmony_ci
2751cb0ef41Sopenharmony_ci/**
2761cb0ef41Sopenharmony_ci * Adapted from String.prototype.endsWith polyfill.
2771cb0ef41Sopenharmony_ci */
2781cb0ef41Sopenharmony_ci
2791cb0ef41Sopenharmony_cifunction endsWith(target, searchString, position) {
2801cb0ef41Sopenharmony_ci  position = position || target.length;
2811cb0ef41Sopenharmony_ci  position = position - searchString.length;
2821cb0ef41Sopenharmony_ci  let lastIndex = target.lastIndexOf(searchString);
2831cb0ef41Sopenharmony_ci  return lastIndex !== -1 && lastIndex === position;
2841cb0ef41Sopenharmony_ci}
2851cb0ef41Sopenharmony_ci
2861cb0ef41Sopenharmony_ci
2871cb0ef41Sopenharmony_cifunction toArray(items, columnNames) {
2881cb0ef41Sopenharmony_ci  if (Array.isArray(items)) return items
2891cb0ef41Sopenharmony_ci  let rows = []
2901cb0ef41Sopenharmony_ci  for (let key in items) {
2911cb0ef41Sopenharmony_ci    let item = {}
2921cb0ef41Sopenharmony_ci    item[columnNames[0] || 'key'] = key
2931cb0ef41Sopenharmony_ci    item[columnNames[1] || 'value'] = items[key]
2941cb0ef41Sopenharmony_ci    rows.push(item)
2951cb0ef41Sopenharmony_ci  }
2961cb0ef41Sopenharmony_ci  return rows
2971cb0ef41Sopenharmony_ci}
298