UNPKG

@felisdiligens/md-table-tools

Version:

MultiMarkdown table tools

125 lines (112 loc) 4.48 kB
import { Table, TableCell } from "./table.js"; import { TableParser } from "./tableParser.js"; import { TableRenderer } from "./tableRenderer.js"; /* * Due to the nature of CSV tables, some data will be lost when converting MMD (or HTML) to CSV. * * CSV file specifications and implementation: * https://www.rfc-editor.org/rfc/rfc4180 * http://super-csv.github.io/super-csv/csv_specification.html */ export class CSVTableParser implements TableParser { public constructor( public separator = ",", public quote = "\"", public assumeFirstLineIsHeader = true) { } public parse(table: string): Table { /* Prepare csv string: */ let csv = table.replace(/\r?\n/g, "\n"); if (!csv.endsWith("\n")) csv += "\n"; /* Parse csv string: */ let parsedTable = new Table(); let tableRow = parsedTable.addRow(); tableRow.isHeader = this.assumeFirstLineIsHeader; let cellContent: string = ""; let rowIndex = 0; let colIndex = 0; let isQuoted = false; let lastChar = null; for (const char of csv) { // Comma or newline: if ((char == this.separator || char == "\n") && !isQuoted) { // Get column: let tableColumn; if (rowIndex == 0) tableColumn = parsedTable.addColumn(); else tableColumn = parsedTable.getColumn(colIndex); // Set table cell content: let tableCell = new TableCell(parsedTable, tableRow, tableColumn); tableCell.setText(cellContent); parsedTable.addCell(tableCell); //parsedTable.getCellByObjs(tableRow, tableColumn).setText(cellContent); cellContent = ""; colIndex++; // If it's a newline: if (char == "\n") { // Add a new row to the table: tableRow = parsedTable.addRow(); rowIndex++; colIndex = 0; } } else if (char == this.quote) { if (!isQuoted && lastChar == this.quote) { cellContent += this.quote; } isQuoted = !isQuoted; } else { cellContent += char; } lastChar = char; } // Remove unused row: parsedTable.removeRow(tableRow); return parsedTable; } } /** changes the output of CSVTableRenderer */ export enum CSVTableRendererMode { OmitSpecialCharacters, EscapeWithQuotes, AlwaysUseQuotes } export class CSVTableRenderer implements TableRenderer { public constructor( public separator = ",", public quote = "\"", public lineBreak = "\r\n", public mode = CSVTableRendererMode.EscapeWithQuotes) { } public render(table: Table): string { let specialCharactersRegex = new RegExp(`([${this.separator}${this.quote}]|\r\n|\n)`); let specialCharactersRegexGlobal = new RegExp(`([${this.separator}${this.quote}]|\r\n|\n)`, "g"); let quoteRegex = new RegExp(this.quote, "g"); let csv: string[] = []; for (const row of table.getRows()) { let renderedRow: string[] = []; for (const cell of table.getCellsInRow(row)) { switch (this.mode) { case CSVTableRendererMode.AlwaysUseQuotes: renderedRow.push(`${this.quote}${cell.text.replace(quoteRegex, this.quote.repeat(2))}${this.quote}`); break; case CSVTableRendererMode.EscapeWithQuotes: if (specialCharactersRegex.test(cell.text)) { renderedRow.push(`${this.quote}${cell.text.replace(quoteRegex, this.quote.repeat(2))}${this.quote}`); } else { renderedRow.push(cell.text); } break; case CSVTableRendererMode.OmitSpecialCharacters: renderedRow.push(cell.text.replace(specialCharactersRegexGlobal, "")); break; } } csv.push(renderedRow.join(this.separator)); } return csv.join(this.lineBreak); } }