read-excel-file
Version:
Read `.xlsx` files in a web browser or in Node.js
106 lines (101 loc) • 3.91 kB
JavaScript
import parseXmlStream from './parseXmlStream.js';
import InvalidSpreadsheetError from '../xlsx/InvalidSpreadsheetError.js';
/**
* Parses XML markup by calling the supplied callback functions.
* @param {string} xml
* @param {any} state — The initial `state`. This `state` will supposedly be modified by the callback functions as the XML is being parsed.
* @param {function} [onOpenTag]
* @param {function} [onCloseTag]
* @param {function} [onText]
* @param {function} [onProgress] — If defined, will be called every time it finishes parsing yet another chunk of XML.
* @returns {Promise<void>} Returns a `Promise` that resolves to nothing. Inspect the passed `state` argument for changes.
*/
export default function parseXml(xml, state, onOpenTag, onCloseTag, onText, onProgress) {
var parser = parseXmlStream(state, onOpenTag, onCloseTag, onText);
if (onProgress) {
parseXmlInChunks(parser, xml, onProgress);
} else {
// Parse XML "all at once"
parser.write(xml);
parser.end();
}
return parser.promise.then(function (result) {
return result;
}, function (error) {
// If the error is not re-thrown here, the parser will simply keep parsing,
// i.e. it doesn't halt after encountering an error and doesn't require manual resuming.
// So it must re-throw the error here in order for the parser to stop.
// Because the error is thrown inside a `new Promise()` constructor,
// the promise will reject automatically.
var spreadsheetError = new InvalidSpreadsheetError(error.message);
spreadsheetError.stack = error.stack;
spreadsheetError.cause = error;
throw spreadsheetError;
});
// This function is placed here inside the wrapper function body
// in order to not to hav `worker-f`
/**
* Parses XML in chunks.
* @param {object} parser — An object returned from `parseXmlStream()` function.
* @param {string} xml
* @param {function} [onProgress] — Will be called after yet another chunk of XML has been parsed.
* @param {boolean} [nonBlocking] — If `true` is passed then it won't block the current thread while parsing. Otherwise, it will block the current thread until all the XML is parsed.
*/
function parseXmlInChunks(parser, xml, onProgress, nonBlocking) {
var MAX_CHUNK_PROCESSING_TIME = 7; // 16 ms — 60 fps, 7 ms — 144 fps
var INITIAL_CHUNK_SIZE = 64 * 1024; // 64 KB
var chunksCount = 0;
var chunkSize = INITIAL_CHUNK_SIZE;
/**
* Parses next chunk of XML.
* @returns {boolean} Returns `true` if there're more chunks to write.
*/
var parseNextChunk = function parseNextChunk() {
chunksCount++;
var startedAt = Date.now();
if (xml.length > chunkSize) {
parser.write(xml.slice(0, chunkSize));
if (onProgress) {
onProgress(false);
}
xml = xml.slice(chunkSize);
var chunkProcessingTime = Date.now() - startedAt;
if (chunkProcessingTime < MAX_CHUNK_PROCESSING_TIME * 0.5) {
chunkSize *= 2;
} else if (chunkProcessingTime > MAX_CHUNK_PROCESSING_TIME) {
chunkSize /= 2;
}
return true;
} else {
parser.write(xml);
parser.end();
if (onProgress) {
onProgress(true);
}
return false;
}
};
/**
* Parses next chunk of XML and then repeats.
*/
var loop = function loop() {
if (parseNextChunk()) {
if (nonBlocking) {
if (typeof setImmediate !== 'undefined') {
setImmediate(loop);
} else {
setTimeout(loop, 0);
}
} else {
loop();
}
} else {
// Finished
// console.log('* chunk size', chunkSize)
// console.log('* chunks count', chunksCount)
}
};
loop();
}
}
//# sourceMappingURL=parseXml.js.map