UNPKG

@worker-tools/html-rewriter

Version:

WASM-based implementation of Cloudflare's HTML Rewriter for use in Deno, browsers, etc.

109 lines 5.31 kB
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var _HTMLRewriter_elementHandlers, _HTMLRewriter_documentHandlers, _a; // FIXME: replace multiple 1 import from skypack!? import "./_dnt.polyfills.js"; import * as _base from './vendor/html_rewriter.js'; const { default: initWASM } = _base; const base = _base; import { ResolvablePromise } from '@worker-tools/resolvable-promise'; const kEnableEsiTags = Symbol("kEnableEsiTags"); // In case a server doesn't return the proper mime type (e.g. githubusercontent.com).. const toWASMResponse = (response) => { var _b; if ((_b = response.headers.get('content-type')) === null || _b === void 0 ? void 0 : _b.startsWith('application/wasm')) return response; const { body, headers: hs, ...props } = response; const headers = new Headers(hs); headers.set('content-type', 'application/wasm'); return new Response(body, { ...props, headers }); }; const initialized = new ResolvablePromise(); let executing = false; export class HTMLRewriter { constructor() { _HTMLRewriter_elementHandlers.set(this, []); _HTMLRewriter_documentHandlers.set(this, []); Object.defineProperty(this, _a, { enumerable: true, configurable: true, writable: true, value: false }); if (!initialized.settled && !executing) { executing = true; fetch(new URL("./vendor/html_rewriter_bg.wasm", import.meta.url).href) .then(r => r.ok ? r : (() => { throw Error('WASM response not ok'); })()) .then(toWASMResponse) .then(initWASM) .then(() => initialized.resolve()) .catch(err => { executing = false; console.error(err); }); } } on(selector, handlers) { __classPrivateFieldGet(this, _HTMLRewriter_elementHandlers, "f").push([selector, handlers]); return this; } onDocument(handlers) { __classPrivateFieldGet(this, _HTMLRewriter_documentHandlers, "f").push(handlers); return this; } transform(response) { const body = response.body; // HTMLRewriter doesn't run the end handler if the body is null, so it's // pointless to setup the transform stream. if (body === null) return new Response(body, response); if (response instanceof Response) { // Make sure we validate chunks are BufferSources and convert them to // Uint8Arrays as required by the Rust glue code. response = new Response(response.body, response); } let rewriter; const transformStream = new TransformStream({ start: async (controller) => { // Create a rewriter instance for this transformation that writes its // output to the transformed response's stream. Note that each // BaseHTMLRewriter can only be used once. await initialized; rewriter = new base.HTMLRewriter((output) => { // enqueue will throw on empty chunks if (output.length !== 0) controller.enqueue(output); }, { enableEsiTags: this[kEnableEsiTags] }); // Add all registered handlers for (const [selector, handlers] of __classPrivateFieldGet(this, _HTMLRewriter_elementHandlers, "f")) { rewriter.on(selector, handlers); } for (const handlers of __classPrivateFieldGet(this, _HTMLRewriter_documentHandlers, "f")) { rewriter.onDocument(handlers); } }, // The finally() below will ensure the rewriter is always freed. // chunk is guaranteed to be a Uint8Array as we're using the // @miniflare/core Response class, which transforms to a byte stream. transform: (chunk) => rewriter.write(chunk), flush: () => rewriter.end(), }); const promise = body.pipeTo(transformStream.writable); promise.catch(() => { }).finally(() => rewriter === null || rewriter === void 0 ? void 0 : rewriter.free()); // Return a response with the transformed body, copying over headers, etc const res = new Response(transformStream.readable, response); // If Content-Length is set, it's probably going to be wrong, since we're // rewriting content, so remove it res.headers.delete("Content-Length"); return res; } } _HTMLRewriter_elementHandlers = new WeakMap(), _HTMLRewriter_documentHandlers = new WeakMap(), _a = kEnableEsiTags; export function withEnableEsiTags(rewriter) { rewriter[kEnableEsiTags] = true; return rewriter; } //# sourceMappingURL=index.js.map