UNPKG

@fontoxml/fontoxml-development-tools

Version:

Development tools for Fonto.

194 lines (171 loc) 4.85 kB
import fs from 'fs'; import os from 'os'; import path from 'path'; function escapeCellForTabSeperatedOutput(cell) { if (cell === undefined || cell === null) { return ''; } const cellAsString = `${cell}`; if (cellAsString.match(/("|\r|\n|\t)/)) { return `"${cellAsString.replace(/"/g, '""')}"`; } return cell; } function tabSeperated(tableData) { return ( [tableData.columns.map((col) => col.label)] .concat(tableData.rows) .map((row) => { return row.map(escapeCellForTabSeperatedOutput).join('\t'); }) .join(os.EOL) + os.EOL ); } function escapeCellForCommaSeperatedOutput(cell) { if (cell === undefined || cell === null) { return '""'; } const cellAsString = `${cell}`; return `"${cellAsString.replace(/"/g, '""')}"`; } function commaSeperated(tableData) { return `${ [tableData.columns.map((col) => col.label)] .concat(tableData.rows) .map((row) => { return row.map(escapeCellForCommaSeperatedOutput).join(','); }) .join(os.EOL) // Use \r\n rather than os.EOL as per RFC 4180 }\r\n`; } function jsonFormatted(tableData) { return ( JSON.stringify( tableData.rows.map((row) => tableData.columns.reduce((obj, col, i) => { obj[col.name] = row[i]; return obj; }, {}) ), null, '\t' ) + os.EOL ); } function getSortIndexForInput(sortColumnIndex, visibleColumnDefinitions) { return isNaN(parseInt(sortColumnIndex, 10)) ? Math.max( visibleColumnDefinitions.findIndex( (column) => column.name === sortColumnIndex ), 0 ) : parseInt(sortColumnIndex, 10); } function getColumnDefinitionsForInput(columns, allColumns) { return columns .map((columnName) => allColumns.find((column) => column.name === columnName) ) .filter((column) => !!column); } function getData(visibleColumnDefinitions, data, sortIndex) { return { columns: visibleColumnDefinitions.map((column, i) => Object.assign(column, { isSorted: i === sortIndex }) ), rows: data .map((operation) => visibleColumnDefinitions.map((column) => column.value(operation) ) ) .sort((a, b) => a[sortIndex] === b[sortIndex] ? 0 : a[sortIndex] < b[sortIndex] ? -1 : 1 ), }; } const exportTransformers = { csv: commaSeperated, xls: tabSeperated, json: jsonFormatted, }; export default class FdtTable { constructor(moduleRegistration, columns) { this.columns = columns; this.sortOption = new moduleRegistration.Option('sort') .setShort('S') .setDescription( 'Column name or number to sort by (defaults to 0, first column).' ) .setDefault('0', true); const columnNames = columns .map((col) => col.name + (col.default ? '*' : '')) .join('|'); this.columnsOption = new moduleRegistration.MultiOption('columns') .setDescription( `One or more space-separated column names to output (${columnNames}), only works when not exporting.` ) .setShort('C') .setDefault( columns.filter((col) => col.default).map((col) => col.name), true ); const exportFormats = Object.keys(exportTransformers) .map((col, i) => col + (!i ? '*' : '')) .join('|'); this.exportOption = new moduleRegistration.Option('export') .setDescription( `Export table to a file; the export type is determined by the file extension (${exportFormats}), and ignores the columns option.` ) .setShort('E'); } print(res, columnsInput, data, sortInput, exportLocation) { const visibleColumnDefinitions = exportLocation ? this.columns : getColumnDefinitionsForInput(columnsInput, this.columns); const tableData = getData( visibleColumnDefinitions, data, getSortIndexForInput(sortInput, visibleColumnDefinitions) ); if (!exportLocation) { res.table( tableData.columns.map( (col) => col.label + (col.isSorted ? '*' : '') ), tableData.rows.map((row) => row.map((cell) => cell || '-')) ); res.break(); const columnLabels = visibleColumnDefinitions .map((col, _i) => col.label + (col.isSorted ? '*' : '')) .join(', ') .toLowerCase(); res.success( `Printed ${columnLabels} for ${tableData.rows.length} results` ); return; } const ext = path .extname(path.basename(exportLocation)) .replace('.', ''); if (!exportTransformers[ext]) { throw new res.InputError( `Unknown export type "${ext}".`, `You can export a table by using the "export" option to specify a file with one of the following extensions: ${Object.keys( exportTransformers ).join('|')}.` ); } res.debug(`Exporting to "${exportLocation}"`); const exported = exportTransformers[ext](tableData); // Notice this favours process.cwd() over app.processPath, which is not necessarily the same fs.writeFileSync(exportLocation, exported); res.debug(`Exported file: ${exported.length} characters`); } }