import type { HTMLRewriter as BaseHTMLRewriter, ContentTypeOptions, Element, EndTag, Comment, TextChunk, Doctype, DocumentEnd, ElementHandlers, DocumentHandlers,} from "./vendor/html_rewriter.d.ts";import * as _base from './vendor/html_rewriter.js'const { default: initWASM } = _base;const base: typeof import("./vendor/html_rewriter.d.ts") = _base;
export type { ContentTypeOptions, Element, EndTag, Comment, TextChunk, Doctype, DocumentEnd, ElementHandlers, DocumentHandlers,}
import { ResolvablePromise } from 'https://ghuc.cc/worker-tools/resolvable-promise/index.ts'
type SelectorElementHandlers = [selector: string, handlers: ElementHandlers];
const kEnableEsiTags = Symbol("kEnableEsiTags");
const toWASMResponse = (response: Response) => { if (response.headers.get('content-type')?.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<void>();let executing = false;
export class HTMLRewriter { readonly #elementHandlers: SelectorElementHandlers[] = []; readonly #documentHandlers: DocumentHandlers[] = []; [kEnableEsiTags] = false;
constructor() { 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: string, handlers: ElementHandlers): this { this.#elementHandlers.push([selector, handlers]); return this; }
onDocument(handlers: DocumentHandlers): this { this.#documentHandlers.push(handlers); return this; }
transform(response: Response): Response { const body = response.body as ReadableStream<Uint8Array> | null; if (body === null) return new Response(body, response);
if (response instanceof Response) { response = new Response(response.body, response); }
let rewriter: BaseHTMLRewriter; const transformStream = new TransformStream<Uint8Array, Uint8Array>({ start: async (controller) => { await initialized; rewriter = new base.HTMLRewriter( (output) => { if (output.length !== 0) controller.enqueue(output); }, { enableEsiTags: this[kEnableEsiTags] } ); for (const [selector, handlers] of this.#elementHandlers) { rewriter.on(selector, handlers); } for (const handlers of this.#documentHandlers) { rewriter.onDocument(handlers); } }, transform: (chunk) => rewriter.write(chunk), flush: () => rewriter.end(), }); const promise = body.pipeTo(transformStream.writable); promise.catch(() => {}).finally(() => rewriter?.free());
const res = new Response(transformStream.readable, response); res.headers.delete("Content-Length"); return res; }}
export function withEnableEsiTags(rewriter: HTMLRewriter): HTMLRewriter { rewriter[kEnableEsiTags] = true; return rewriter;}