@felisdiligens/md-table-tools
Version:
MultiMarkdown table tools
125 lines (112 loc) • 4.48 kB
text/typescript
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);
}
}