@oniryk/xlsx
Version:
A lightweight, efficient TypeScript library for generating single-sheet Excel XLSX files with support for large datasets
148 lines (147 loc) • 4.33 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.escapeXML = escapeXML;
exports.convertToExcelDate = convertToExcelDate;
exports.simpleDateFormat = simpleDateFormat;
exports.isValidDate = isValidDate;
exports.destructiveStream = destructiveStream;
exports.perf = perf;
exports.promiseWrite = promiseWrite;
exports.finishStream = finishStream;
const fs_1 = require("fs");
/** XML entity mapping for special characters */
const entities = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
};
/**
* Escapes special characters in a string for XML content
* Converts &, <, >, ", and ' to their XML entity equivalents
*
* @param value - Value to escape
* @returns Escaped string if input is string, otherwise returns original value
*
* @example
* escapeXML('Hello < World') // returns 'Hello < World'
*/
function escapeXML(value) {
if (typeof value !== 'string')
return value;
return value.replace(/[\x00-\x1F\x7F]/g, '?').replace(/[&<>"'\\]/g, (char) => {
return entities[char];
});
}
/** Excel epoch date (December 30, 1899) used for date conversions */
const EPOCH = new Date(1899, 11, 30, 0, 0, 0).getTime();
/** Milliseconds in a day */
const DAY_MS = 24 * 60 * 60 * 1000;
/**
* Converts a JavaScript Date to Excel serial number format
* Excel uses days since December 30, 1899 as its internal date representation
*
* @param date - JavaScript Date object to convert
* @returns Number of days since Excel epoch, with fractional part for time
*/
function convertToExcelDate(date) {
return Math.round(((date.getTime() - EPOCH) / DAY_MS) * 100000) / 100000;
}
/**
* Formats a date as YYYY-MM-DD
*
* @param date - Date to format
* @returns Date string in YYYY-MM-DD format
*
* @example
* simpleDateFormat(new Date('2024-01-31')) // returns '2024-01-31'
*/
function simpleDateFormat(date) {
return date.toISOString().slice(0, 10);
}
/**
* Type guard to check if a value is a valid Date object
*
* @param value - Value to check
* @returns True if value is a Date instance
*/
function isValidDate(value) {
return value instanceof Date;
}
/**
* Creates a read stream that automatically deletes the file when finished
* Useful for temporary file handling
*
* @param filePath - Path to the file to stream
* @returns ReadStream that will delete the source file when complete
*/
function destructiveStream(filePath) {
const stream = (0, fs_1.createReadStream)(filePath);
stream.on('end', () => {
stream.destroy();
(0, fs_1.unlink)(filePath, (err) => {
if (err)
console.error('Error deleting file:', err);
});
});
stream.on('error', (error) => {
stream.destroy();
console.error('Stream error:', error);
});
return stream;
}
/**
* Simple performance measurement utility
*
* @param label - Label to identify the performance measurement
* @returns Object with end function to log elapsed time
*
* @example
* const timer = perf('operation');
* // ... do something ...
* timer.end(); // logs: "operation: XXXms"
*/
function perf(label) {
const start = performance.now();
return {
end: () => {
console.log(`${label}: ${(performance.now() - start).toFixed(2)}ms`);
},
};
}
/**
* Creates a promise-based writer function for a WriteStream
* Allows for easier async/await usage of stream writes
*
* @param stream - WriteStream to wrap
* @returns Promise-based write function
*
* @example
* const writer = promiseWrite(writeStream);
* await writer('content1', 'content2');
*/
function promiseWrite(stream) {
return (...content) => {
return new Promise((resolve, reject) => {
stream.write(content.join(''), (err) => (err ? reject(err) : resolve()));
});
};
}
/**
* Safely finishes a write stream
* Returns a promise that resolves when the stream is fully closed
*
* @param stream - WriteStream to finish
* @returns Promise that resolves when stream is finished
*/
function finishStream(stream) {
return new Promise((resolve, reject) => {
stream.on('finish', () => {
stream.destroy();
resolve();
});
stream.on('error', reject);
stream.end();
});
}