@aegisjsproject/parsers
Version:
A collection of secure & minimal parsers for HTML, CSS, SVG, MathML, XML, and JSON
221 lines (186 loc) • 6.77 kB
JavaScript
;
var base_js = require('@aegisjsproject/sanitizer/config/base.js');
var mathml_js = require('@aegisjsproject/sanitizer/config/mathml.js');
var svg_js = require('@aegisjsproject/sanitizer/config/svg.js');
var parser_js = require('@aegisjsproject/url/parser.js');
const stringify$1 = thing => {
switch(typeof thing) {
case 'undefined':
return '';
case 'boolean':
return thing ? 'true' : 'false';
case 'object':
if (thing === null) {
return '';
} else if (thing instanceof CSSStyleSheet) {
return [...thing.cssRules].map(rule => rule.cssText).join('\n\n');
} else if (thing instanceof CSSRule) {
return thing.cssText;
} else if (thing instanceof HTMLLinkElement) {
return stringify$1(thing.sheet);
} else if (thing instanceof ArrayBuffer && Uint8Array.prototype.toBase64 instanceof Function) {
return new Uint8Array(thing).toBase64();
} else if (ArrayBuffer.isView(thing) && thing.toBase64 instanceof Function) {
return thing.toBase64();
} else if (thing instanceof Blob) {
return URL.createObjectURL(thing);
} else {
return thing.toString();
}
default:
return thing.toString();
}
};
function createStyleSheet(cssRules, { media, disabled, baseURL } = {}) {
const sheet = new CSSStyleSheet({
media: media instanceof MediaQueryList ? media.media : media,
disabled,
baseURL
});
sheet.replace(cssRules).catch(reportError);
return sheet;
}
function createStyleSheetSync(cssRules, { media, disabled, baseURL } = {}) {
const sheet = new CSSStyleSheet({
media: media instanceof MediaQueryList ? media.media : media,
disabled,
baseURL
});
sheet.replaceSync(cssRules);
return sheet;
}
const createCSSParser = ({ media, disabled, baseURL, sync = false } = {}) => sync
? (strings, ...args) => createStyleSheetSync(String.raw(strings, ...args.map(stringify$1)).trim(), { media, disabled, baseURL })
: (strings, ...args) => createStyleSheet(String.raw(strings, ...args.map(stringify$1)).trim(), { media, disabled, baseURL });
const css = createCSSParser();
function styleSheetToLink(sheet, { crossOrigin = 'anonymous', referrerPolicy = 'no-referrer' } = {}) {
const link = document.createElement('link');
const file = new File(Array.from(sheet.cssRules, rule => rule.cssText), 'sheet.css', { type: sheet.type });
link.rel = 'stylesheet';
link.crossOrigin = crossOrigin;
link.referrerPolicy = referrerPolicy;
link.disabled = sheet.disabled;
if (sheet.media.length !== 0) {
link.media = sheet.media.mediaText;
}
link.href = URL.createObjectURL(file);
return link;
}
function setStyleSheets(node, ...styles) {
if (node instanceof HTMLDocument || node instanceof ShadowRoot) {
node.adoptedStyleSheets = styles;
} else {
throw new TypeError('Node must be a `HTMLDocument` or `ShadowRoot`.');
}
}
function addStyleSheets(node, ...styles) {
if (node instanceof Document || node instanceof ShadowRoot) {
node.adoptedStyleSheets = [...node.adoptedStyleSheets, ...styles];
} else {
throw new TypeError('Node must be a `HTMLDocument` or `ShadowRoot`.');
}
}
const stringifyChildren = thing => Array.from(
thing[Symbol.iterator](),
node => node.nodeType === Node.ELEMENT_NODE
? node.outerHTML
: node.textContent
).join('');
function stringify(thing) {
switch(typeof thing) {
case 'string':
return thing;
case 'function':
throw new TypeError('Functions are not supported.');
case 'undefined':
return '';
case 'object':
if (thing === null) {
return '';
} else if (thing instanceof DocumentFragment || thing instanceof Document) {
return stringifyChildren(thing.childNodes);
} else if (thing instanceof NodeList || thing instanceof HTMLCollection) {
return stringifyChildren(thing);
} else if (thing instanceof Element) {
return thing.outerHTML;
} else if (thing instanceof Date) {
return thing.toISOString();
} else if (Array.isArray(thing)) {
return thing.map(stringify).join('');
} else if (thing instanceof ArrayBuffer && Uint8Array.prototype.toBase64 instanceof Function) {
return new Uint8Array(thing).toBase64();
} else if (ArrayBuffer.isView(thing) && thing.toBase64 instanceof Function) {
return thing.toBase64();
} else if (thing instanceof Blob) {
return URL.createObjectURL(thing);
} else {
return thing.toString();
}
default:
return thing.toString();
}
}
function createHTMLParser(config = base_js.sanitizer, { mapper = stringify } = {}) {
return (strings, ...values) => {
const frag = document.createDocumentFragment();
const tmp = document.createElement('div');
tmp.setHTML(String.raw(strings, ...values.map(mapper)).trim(), config);
frag.append(...tmp.childNodes);
return frag;
};
}
const html = createHTMLParser(base_js.sanitizer, { mapper: stringify });
const el = (...args) => html.apply(null, args).firstElementChild;
function doc(strings, ...values) {
return Document.parseHTML(String.raw(strings, ...values.map(stringify)), base_js.sanitizer);
}
const json = (...args) => JSON.parse(String.raw.apply(null, args).trim());
function math(...args) {
return Document.parseHTML(
String.raw.apply(null, args).trim(),
{ sanitizer: { elements: ['html', 'head', 'body', ...mathml_js.elements], attributes: mathml_js.attributes }}
).body.firstElementChild;
}
function svg(...args) {
return Document.parseHTML(
String.raw.apply(null, args).trim(),
{ sanitizer: { elements: ['html', 'head', 'body', ...svg_js.elements], attributes: svg_js.attributes }}
).body.firstElementChild;
}
function createPolicy(name = 'aegis-parsers#html', {
elements = base_js.sanitizer.elements,
attributes = base_js.sanitizer.attributes,
comments = base_js.sanitizer.comments,
} = base_js.sanitizer) {
const createHTML = (input) => {
const el = document.createElement('div');
el.setHTML(input, { sanitizer: { elements, attributes, comments }});
return el.innerHTML;
};
if ('trustedTypes' in globalThis) {
return globalThis.trustedTypes.createPolicy(name, { createHTML });
} else {
return Object.freeze({ createHTML });
}
}
const xml = (...args) => new DOMParser()
.parseFromString(String.raw.apply(null, args).trim(), 'application/xml');
Object.defineProperty(exports, "url", {
enumerable: true,
get: function () { return parser_js.url; }
});
exports.addStyleSheets = addStyleSheets;
exports.createCSSParser = createCSSParser;
exports.createHTMLParser = createHTMLParser;
exports.createPolicy = createPolicy;
exports.createStyleSheet = createStyleSheet;
exports.css = css;
exports.doc = doc;
exports.el = el;
exports.html = html;
exports.json = json;
exports.math = math;
exports.setStyleSheets = setStyleSheets;
exports.styleSheetToLink = styleSheetToLink;
exports.svg = svg;
exports.xml = xml;