SPB Git forge

spb/rareindex

Public
54commits 1branches 0releases
7.1 MBsize
maindefault branch
10 days agolast push
TypeScript 61.9% HTML 37.2% SQL 0.7%
2.6 KB · 88 lines typescript
Raw Blame History
1import * as cheerio from 'cheerio';23/** Thin HTML helpers shared by connectors (cheerio). */4export function load(html: string) {5  return cheerio.load(html);6}78export type CheerioRoot = ReturnType<typeof cheerio.load>;910export function text($el: { text(): string } | null | undefined): string | null {11  const t = $el?.text()?.replace(/\s+/g, ' ').trim();12  return t ? t : null;13}1415export function absUrl(base: string, href: string | null | undefined): string | null {16  if (!href) return null;17  try {18    return new URL(href, base).toString();19  } catch {20    return null;21  }22}2324/** Extract JSON-LD blocks of a given @type (e.g. Product, Offer) from HTML. */25export function jsonLd(html: string, type?: string): Record<string, unknown>[] {26  const $ = cheerio.load(html);27  const out: Record<string, unknown>[] = [];28  $('script[type="application/ld+json"]').each((_, el) => {29    const raw = $(el).contents().text();30    try {31      const parsed = JSON.parse(raw) as unknown;32      const items = Array.isArray(parsed) ? parsed : (parsed && typeof parsed === 'object' && '@graph' in (parsed as object) ? ((parsed as { '@graph': unknown[] })['@graph'] ?? []) : [parsed]);33      for (const it of items) {34        if (it && typeof it === 'object') {35          const t = (it as { '@type'?: string | string[] })['@type'];36          const types = Array.isArray(t) ? t : t ? [t] : [];37          if (!type || types.includes(type)) out.push(it as Record<string, unknown>);38        }39      }40    } catch {41      /* ignore malformed JSON-LD */42    }43  });44  return out;45}4647/** Extract Next.js __NEXT_DATA__ payload when present. */48export function nextData(html: string): unknown | null {49  const m = html.match(/<script id="__NEXT_DATA__"[^>]*>([\s\S]*?)<\/script>/);50  if (!m) return null;51  try {52    return JSON.parse(m[1]!);53  } catch {54    return null;55  }56}5758/** Extract a JS object literal assigned to a global, e.g. window.__INITIAL_STATE__ = {...}; */59export function inlineJson(html: string, marker: string): unknown | null {60  const idx = html.indexOf(marker);61  if (idx < 0) return null;62  const start = html.indexOf('{', idx);63  if (start < 0) return null;64  let depth = 0;65  let inStr: string | null = null;66  for (let i = start; i < html.length; i++) {67    const ch = html[i]!;68    if (inStr) {69      if (ch === '\\') i++;70      else if (ch === inStr) inStr = null;71      continue;72    }73    if (ch === '"' || ch === "'") inStr = ch;74    else if (ch === '{') depth++;75    else if (ch === '}') {76      depth--;77      if (depth === 0) {78        try {79          return JSON.parse(html.slice(start, i + 1));80        } catch {81          return null;82        }83      }84    }85  }86  return null;87}88