@oniryk/xlsx
Version:
A lightweight, efficient TypeScript library for generating single-sheet Excel XLSX files with support for large datasets
157 lines (156 loc) • 5.93 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const crypto_1 = require("crypto");
const fs_1 = __importDefault(require("fs"));
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
const column_js_1 = require("./column.js");
const utils_js_1 = require("./utils.js");
/**
* Maps column properties to their XML representation
* @param k - Column index (0-based)
* @param w - Column width
* @returns XML string representing column properties
*/
const mapper = ([k, w]) => {
return `<col min="${k + 1}" max="${k + 1}" width="${w}" customWidth="1"/>`;
};
/**
* Represents an Excel worksheet
* Handles the creation and management of worksheet data including rows, columns, and cell formatting
*/
class Sheet {
/**
* Creates a new Sheet instance
* @param sharedStrings - SharedStrings instance for managing string deduplication
*/
constructor(sharedStrings) {
/** Storage for worksheet rows */
this.rows = [];
/** Map of column indices to their widths */
this.columnWidths = new Map();
this.sharedStrings = sharedStrings;
this.file = path_1.default.join(os_1.default.tmpdir(), `xlsx-${(0, crypto_1.randomUUID)()}.xml`);
}
/**
* Adds a single row to the worksheet
* @param row - Array of cell values
*/
addRow(row) {
this.rows.push(row);
}
/**
* Adds multiple rows to the worksheet
* @param rows - Array of rows to add
*/
addRows(rows) {
this.rows = [...this.rows, ...rows];
}
/**
* Gets the total number of rows in the worksheet
* @returns Number of rows
*/
rowsCount() {
return this.rows.length;
}
setColumWidth(indexOrSizes, width) {
if (Array.isArray(indexOrSizes)) {
for (const [index, width] of indexOrSizes) {
this.columnWidths.set(index, width);
}
}
else if (typeof indexOrSizes === 'number' && typeof width === 'number') {
this.columnWidths.set(indexOrSizes, width);
}
}
/**
* Gets the Excel column name for a given index
* @param index - Zero-based column index
* @returns Excel column name (e.g., 'A', 'B', 'AA')
* @private
*/
getExcelColumn(index) {
return Sheet.COLUMNS.get(index) || (0, column_js_1.columnName)(index);
}
/**
* Writes a chunk of rows to the worksheet XML
* @param write - Promise-based writer function
* @param rows - Array of rows to write
* @param startIndex - Starting row index for this chunk
* @returns Promise that resolves when chunk is written
* @private
*/
async writeChunk(write, rows, startIndex) {
const add = (str) => this.sharedStrings.add(str);
const chunks = [];
for (let i = 0, rlen = rows.length; i < rlen; i += 1) {
const cols = rows[i];
const row = (startIndex + i + 1).toString();
chunks.push(`<row r="${row}">`);
for (let ci = 0, clen = cols.length; ci < clen; ci += 1) {
const cell = cols[ci];
const ref = `${this.getExcelColumn(ci)}${row}`;
if (cell === null || cell === undefined) {
chunks.push(`<c r="${ref}"/>`);
continue;
}
switch (typeof cell) {
case 'string':
chunks.push(`<c r="${ref}" t="s"><v>${add(cell)}</v></c>`);
continue;
case 'number':
chunks.push(`<c r="${ref}"><v>${cell}</v></c>`);
continue;
case 'object':
if ((0, utils_js_1.isValidDate)(cell)) {
chunks.push(`<c r="${ref}" s="1"><v>${(0, utils_js_1.convertToExcelDate)(cell)}</v></c>`);
}
else {
chunks.push(`<c r="${ref}" t="s"><v>${add(String(cell))}</v></c>`);
}
continue;
default:
chunks.push(`<c r="${ref}" t="s"><v>${add(String(cell))}</v></c>`);
}
}
chunks.push('</row>');
}
const content = chunks.join('');
chunks.length = 0;
await write(content);
}
/**
* Generates the complete worksheet XML file
* @returns Promise that resolves to the path of the generated XML file
*/
async generateSheetXML() {
const ws = fs_1.default.createWriteStream(this.file);
const write = (0, utils_js_1.promiseWrite)(ws);
await write('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n', '<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">');
if (this.columnWidths.size > 0) {
await write('<cols>', [...this.columnWidths.entries()].map(mapper).join(''), '</cols>');
}
await write('<sheetData>');
for (let i = 0; i < this.rows.length; i += Sheet.CHUNK_SIZE) {
const chunk = this.rows.slice(i, i + Sheet.CHUNK_SIZE);
await this.writeChunk(write, chunk, i);
}
await write('</sheetData></worksheet>');
await (0, utils_js_1.finishStream)(ws);
return this.file;
}
}
/** Map of pre-calculated column names (A-ZZ) */
Sheet.COLUMNS = new Map();
/** Number of rows to process in each chunk when generating XML */
Sheet.CHUNK_SIZE = 2500;
/** Initialize column names map */
(() => {
for (let i = 0, ZZ = 702; i < ZZ; i += 1) {
Sheet.COLUMNS.set(i, (0, column_js_1.columnName)(i));
}
})();
exports.default = Sheet;