PageSourceSearch

https://blog.tomayac.com/js/streaming-markdown-html/markdown-to-html.js

js tomayac.com collected 2026-09-24 10:05:03 UTC 1,435 bytes, 48 lines download raw bytes

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.