read-excel-file
Version:
Read `.xlsx` files in a web browser or in Node.js
130 lines (120 loc) • 4.91 kB
JavaScript
// Starting from version `11.x`, `saxen` dropped CommonJS export.
// CommonJS compatibility was requested by one of the users of this package:
// https://gitlab.com/catamphetamine/read-excel-file/-/work_items/115
// Because of that, `saxen` source code had to be copy-pasted.
//
// Not to mention that it would have to be copy-pasted anyway for compatiblity with `worker-f`
// because `Parser` is not a "self-contained" function.
//
// import { Parser } from 'saxen'
import Parser from '../saxen/parser.js';
/**
* Parses XML markup in a streaming fashion by calling the supplied callback functions as the XML markup is being input.
* @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]
* @returns {object} An object with properties: `promise`, `write(string)`, `end()`. The `promise` resolves with nothing.
*/
export default function parseXmlStream(state, onOpenTag, onCloseTag, onText) {
var errored = false;
var mustNotHaveErrored = function mustNotHaveErrored() {
if (errored) {
// If this error is thrown then it means that there's a bug in the code
// because the code should not have got here if the XML parsing process
// has already errored.
throw new Error('Errored');
}
};
var resolvePromise;
// const xmlns = false
var parser = new Parser();
// // Whether it should parse XML "namespaces" (`xmlns` stuff).
// // It parses faster when the "namespaces" mode is disabled.
// // That's why it's not enabled.
// if (xmlns) {
// // // See `xml/xlsxNamespaces.js` file for the list of possible `.xlsx` namespaces.
// // parser.ns(xlsxNamespaces)
// parser.ns()
// }
var write = function write(xml) {
mustNotHaveErrored();
parser.write(xml);
};
var end = function end() {
mustNotHaveErrored();
parser.end();
resolvePromise();
};
// This `promise` resolves with the final `state` when finished parsing.
var promise = new Promise(function (resolve, reject) {
resolvePromise = resolve;
// on XML parsing error
var onerror = function onerror(error) {
errored = true;
// 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.
throw error;
};
// got some text. `text` is the string of text.
var ontext = function ontext(text, decodeEntities) {
if (onText) {
// `saxen` doesn't decode character references (`é`, `&`, etc) in text:
// instead, it provides a `decodeEntities()` function for the consumer to call.
onText(decodeEntities(text), state);
}
};
// opened a tag. `node` has "name" and "attributes"
var onopentag = function onopentag(elementName, getAttributes, decodeEntities, selfClosing, getContext) {
if (onOpenTag) {
var attributes = getAttributes();
// `saxen` doesn't decode character references (`é`, `&`, etc)
// in attribute values either, so decode them here.
// The reason why `saxen` deliberately doesn't decode character references by default
// is performance, I assume.
for (var name in attributes) {
// Also remove an `xmlns` prefix from the attribute name, if present.
// When doing so, don't clean up the attribute name with a prefix
// because it doesn't interfere, and it's faster this way.
attributes[trimXmlnsPrefix(name, true)] = decodeEntities(attributes[name]);
}
onOpenTag(trimXmlnsPrefix(elementName), attributes, state);
}
};
// closed a tag.
var onclosetag = function onclosetag(elementName) {
if (onCloseTag) {
onCloseTag(trimXmlnsPrefix(elementName), state);
}
};
parser.on('error', onerror);
parser.on('text', ontext);
parser.on('openTag', onopentag);
parser.on('closeTag', onclosetag);
});
return {
promise: promise,
write: write,
end: end
};
}
function trimXmlnsPrefix(string, isAttributeName) {
var i = 0;
while (i < string.length) {
if (string[i] === ':') {
// If `string` is an attribute name, filter out `xmlns:...` cases
// which aren't really attributes but rather xmlns schema URIs.
if (isAttributeName && i === 5 && string.slice(0, 5) === 'xmlns') {
// Ignore this attribute.
} else {
return string.slice(i + 1);
}
}
i++;
}
return string;
}
//# sourceMappingURL=parseXmlStream.saxen.js.map