UNPKG

@sunney/flareutils

Version:

Small Utilities and little goodies that make developing with Cloudflare easier and faster.

159 lines 6.59 kB
const deleteElement = { element: (e) => { e.remove(); }, }; /** * Simple Workers-native templating engine. Utilizes the [*HTMLRewriter*](http://developers.cloudflare.com/workers/runtime-apis/html-rewriter) API, allowing templating operations to be streamed to the user, rather than being a blocking process. While it is not required, it is recommended that any resulting HTML be cached before being returned. * @template Environment The Environment object, provided by the Workers Runtime. * @template Optional Optional parameters, provided to your replacer function. */ export class Temra { /** * Default Prefix used for tag names. */ tagPrefix; /** * Whether comments should be removed. */ deleteComments; /** * Replacers applied to your HTML. */ replacers = {}; /** * Initializes a new Temra instance. * @param {string} prefix Default Prefix used for tag names. For example, if the prefix is "Temra", then the tag name "TemraName" will be read as "Name". * @param {boolean} deleteComments Whether comments should be removed. */ constructor(prefix, deleteComments) { this.tagPrefix = prefix || ""; this.deleteComments = deleteComments || false; } /** * Add a replacer to the Temra instance. * @param {string} selector Selector used to find elements. * @param {AddOptions} options Options used to configure the replacer. * @returns {Temra} Returns the Temra instance, for chaining. * @example ```ts * temra.add("username", () => "Jay Doe", {removeSelector: true}); * ``` */ add(selector, options) { const replacer = { type: options.selectorType ? options.selectorType : "className", }; if (options.replacerFunction) { replacer.replacerFunction = options.replacerFunction; } if (options.removeSelector) replacer.removeSelector = true; switch (options.selectorType) { case "universal": this.replacers[selector] = replacer; break; case "tagName": this.replacers[`${this.tagPrefix}${selector}`] = replacer; break; case "className": this.replacers[`*.${selector}`] = replacer; break; case "id": this.replacers[`*#${selector}`] = replacer; break; case "attribute": this.replacers[`*[${selector}${options.attributeValue}]`] = replacer; break; default: throw new Error(`Unknown selector type: ${options.selectorType}`); } return this; } /** * Applies the currently added replacer functions to the provided HTML. * @param {Response} body HTML response that will be transformed. * @param {TransformationOptionals<E, O>} [optionals] Optional parameters to pass to the replacer function. * @returns Response * @example ```ts * return temra.transform(await fetch(req), {req, env}); * ``` */ transform(body, optionals) { const rewriter = new HTMLRewriter(); for (const [selector, replacer] of Object.entries(this.replacers)) { if (replacer.delete) { rewriter.on(selector, deleteElement); continue; } const ElementHandler = {}; switch (replacer.type) { case "universal": case "tagName": ElementHandler.element = async (element) => { if (replacer.replacerFunction) { const content = await replacer.replacerFunction(element, optionals); element.replace(content, { html: replacer.isHTML }); } else { element.remove(); } }; break; case "attribute": ElementHandler.element = async (element) => { if (replacer.replacerFunction) { const content = await replacer.replacerFunction(element, optionals); element.replace(content, { html: replacer.isHTML }); if (replacer.removeSelector) element.removeAttribute(selector); } else { element.remove(); } }; break; case "className": ElementHandler.element = async (element) => { if (replacer.replacerFunction) { const content = await replacer.replacerFunction(element, optionals); element.replace(content, { html: replacer.isHTML }); if (replacer.removeSelector) { const allClasses = element.getAttribute("class"); element.setAttribute("class", allClasses .split(" ") .filter((e) => e !== selector) .join(" ")); } } else { element.remove(); } }; break; case "id": ElementHandler.element = async (element) => { if (replacer.replacerFunction) { const content = await replacer.replacerFunction(element, optionals); element.replace(content, { html: replacer.isHTML }); if (replacer.removeSelector) element.removeAttribute("id"); } else { element.remove(); } }; break; } rewriter.on(selector, ElementHandler); } if (this.deleteComments) rewriter.onDocument({ comments: (c) => { c.remove(); }, }); return rewriter.transform(body); } } export * from "./types"; //# sourceMappingURL=index.js.map