1/** 2 * Copyright 2026 Google LLC 3 * SPDX-License-Identifier: Apache-2.0 4 */ 5 6import { createHtmlTokenStreamer } from './renderer.js'; 7 8/** 9 * A `TransformStream` that takes Markdown chunks and emits HTML chunks. 10 * 11 * This is `createHtmlTokenStreamer()` with a stream on either side, which is 12 * what lets a Markdown source reach the page in one expression: 13 * 14 * ```js 15 * await markdownStream 16 * .pipeThrough(markdownToHtml()) 17 * .pipeTo(renderStreamingHTML(output)); 18 * ``` 19 * 20 * Chunk boundaries on the way in don't have to line up with anything: a 21 * construct split across two writes is held until it is complete. 22 * 23 * @param {object} [options] 24 * @param {(detail: {attribute: string, value: string}) => void} [options.onUnsafe] 25 * Called when an `href` or `src` is dropped for having an unsafe scheme. 26 * @returns {TransformStream<string, string>} 27 */ 28export function markdownToHtml({ onUnsafe } = {}) { 29 /** @type {ReturnType<typeof createHtmlTokenStreamer>} */ 30 let streamer; 31 32 return new TransformStream({ 33 start(controller) { 34 streamer = createHtmlTokenStreamer({ 35 onHtml: (html) => controller.enqueue(html), 36 onUnsafe, 37 }); 38 }, 39 transform(markdown) { 40 streamer.write(markdown); 41 }, 42 flush() { 43 // Closes whatever tags the Markdown left open. The controller still 44 // accepts chunks here, so those closing tags make it out. 45 streamer.end(); 46 }, 47 }); 48}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.