UNPKG

@aegisjsproject/parsers

Version:

A collection of secure & minimal parsers for HTML, CSS, SVG, MathML, XML, and JSON

221 lines (186 loc) 6.77 kB
'use strict'; 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;