UNPKG

@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
"use strict"; 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;