TheBird

read.ts

1.5 kB · typescript · 47 lines

1export type View = { page: { kind: string; title: string; props: Record<string, unknown> }; chrome: { route: string; catalog: unknown[]; fly: boolean; now: number } };23const BOX = /<script id="props"[^>]*>([^<]*)<\/script>/g;45const APP = '<div id="app"';67const MARK = " data-body";89function boxed(html: string): string | null {10  let last: string | null = null;11  BOX.lastIndex = 0;12  for (let hit = BOX.exec(html); hit; hit = BOX.exec(html)) last = hit[1] ?? null;13  return last;14}1516function carve(html: string): string | null {17  const app = html.indexOf(APP);18  const mark = html.indexOf(MARK, app < 0 ? 0 : app);19  if (mark < 0) return null;20  const open = html.lastIndexOf("<", mark);21  const name = /^<([a-zA-Z][^\s/>]*)/.exec(html.slice(open, mark))?.[1];22  const head = html.indexOf(">", mark);23  if (!name || head < 0) return null;24  const tags = new RegExp(`</?${name}(?=[\\s/>])`, "g");25  tags.lastIndex = head + 1;26  let depth = 1;27  for (let hit = tags.exec(html); hit; hit = tags.exec(html)) {28    depth += hit[0][1] === "/" ? -1 : 1;29    if (depth === 0) return html.slice(head + 1, hit.index);30  }31  return null;32}3334export function read(html: string): View | null {35  const text = boxed(html);36  if (text === null) return null;37  let view: View;38  try {39    view = JSON.parse(text) as View;40  } catch {41    return null;42  }43  if (!view?.page || !view.chrome) return null;44  const body = carve(html);45  if (body !== null) view.page.props.body = body;46  return view;47}