libxslt-wasm
Version:
JavaScript bindings for libxslt compiled to WebAssembly
103 lines (102 loc) • 4.37 kB
JavaScript
import { XmlDocument } from "./XmlDocument.js";
import { XmlOutputBuffer } from "./XmlOutputBuffer.js";
import { NULL_POINTER } from "../constants.js";
import { xsltApplyStylesheet, xsltLoadStylesheetPI, xsltFreeStylesheet, xsltParseStylesheetDoc, xsltSaveResultTo, } from "../internal/libxslt.js";
import { StringPtrArray } from "../utils/StringPtrArray.js";
// If you see errors when passing params, considering double quoting params so
// they are considered literals rather than XPath expressions
const parseXsltParams = (params) => {
if (params === undefined) {
return NULL_POINTER;
}
const entries = Object.entries(params);
if (entries.length === 0) {
return NULL_POINTER;
}
const paramArray = entries
.map(([key, value]) => [key, String(value)])
.flat(1);
const paramsArrayPtr = StringPtrArray.fromStringArray(paramArray, true).dataOffset;
if (paramsArrayPtr === null) {
throw new Error("Failed to allocate memory for XSLT parameters");
}
return paramsArrayPtr;
};
class XsltStylesheet extends XmlDocument {
/**
* Assuming the XML document is a valid XSLT stylesheet, creates a new
* instance of XsltStylesheet
*/
static async fromXmlDocument(xmlDocument) {
if (xmlDocument.dataOffset === null) {
throw new Error("XML document has already been disposed");
}
const xsltStylesheet = await xsltParseStylesheetDoc(xmlDocument.dataOffset);
return new XsltStylesheet(xsltStylesheet);
}
/**
* Given an XML document with an embedded XSLT stylesheet (e.g. a processing
* instruction like <?xml-stylesheet?>), return the corresponding
* XsltStylesheet instance
*/
static async fromEmbeddedXmlDocument(xmlDocument) {
if (xmlDocument.dataOffset === null) {
throw new Error("XML document has already been disposed");
}
const xsltStylesheetPtr = await xsltLoadStylesheetPI(xmlDocument.dataOffset);
return xsltStylesheetPtr === NULL_POINTER ? null : (new XsltStylesheet(xsltStylesheetPtr));
}
static async fromFileOrUrl(fileOrUrl) {
return this.fromXmlDocument(await super.fromFileOrUrl(fileOrUrl));
}
static async fromString(string) {
return this.fromXmlDocument(await super.fromString(string));
}
// Since `xsltApplyStylesheet()` uses `xmlXPathCompOpEval()` internally for
// params, this must be async. Consider using `applyToOutputBuffer()` or
// `applyToString()` for synchronous operations
async apply(xmlDocument, params) {
if (this.dataOffset === null) {
throw new Error("XSLT stylesheet has already been disposed");
}
else if (xmlDocument.dataOffset === null) {
throw new Error("XML document has already been disposed");
}
const result = await xsltApplyStylesheet(this.dataOffset, xmlDocument.dataOffset, parseXsltParams(params));
if (result === NULL_POINTER) {
throw new Error("Failed to apply XSLT stylesheet to XML document");
}
return new XmlDocument(result);
}
applyToOutputBuffer(xmlDocument) {
if (this.dataOffset === null) {
throw new Error("XSLT stylesheet has already been disposed");
}
else if (xmlDocument.dataOffset === null) {
throw new Error("XML document has already been disposed");
}
const outputBuffer = XmlOutputBuffer.allocate();
if (outputBuffer.dataOffset === null) {
throw new Error("Failed to allocate memory for XML output buffer");
}
const bytesWritten = xsltSaveResultTo(outputBuffer.dataOffset, xmlDocument.dataOffset, this.dataOffset);
if (bytesWritten === -1) {
throw new Error("Failed to save result to XML output buffer");
}
return outputBuffer;
}
applyToString(xmlDocument) {
const outputBuffer = this.applyToOutputBuffer(xmlDocument);
const result = outputBuffer.toString();
outputBuffer.delete();
return result;
}
delete() {
if (this.dataOffset !== null) {
xsltFreeStylesheet(this.dataOffset);
}
this.dataOffset = null;
// Do NOT call `super.delete()` since `xmlFreeDoc()` will be called
}
}
export { XsltStylesheet };