linkedPaths

tcmts.cli.web.linkedPaths · #zppfzp3o · term · runs anywhere

The paths of the app a rendered page links to, as it follows them: an <a href> under the app's base, with no download, target or rel="external" (as useAppLinks leaves to the browser), to a page (no extension but .html). Their query and fragment are the page's to read, so they're not part of the path.

export function linkedPaths(html: string, at: string, base: string): string[] {
  const origin = "http://tcm.static";
  const found = new Set<string>();
  for (const [tag] of html.matchAll(/<a\s[^>]*>/gi)) {
    const attr = (name: string) =>
      new RegExp(`\\s${name}(?:\\s*=\\s*"([^"]*)")?(?=[\\s/>])`, "i").exec(tag!);
    const href = attr("href")?.[1];
    if (href === undefined || attr("download") || /\bexternal\b/.test(attr("rel")?.[1] ?? ""))
      continue;
    if ((attr("target")?.[1] ?? "_self") !== "_self") continue;
    let url: URL;
    try {
      url = new URL(href.replace(/&amp;/g, "&"), `${origin}${at}`);
    } catch {
      continue;
    }
    if (url.origin !== origin) continue;
    const within = !base
      ? url.pathname
      : url.pathname === base
        ? "/"
        : url.pathname.startsWith(`${base}/`)
          ? url.pathname.slice(base.length)
          : undefined;
    if (within === undefined || /\.(?!html$)[^./]+$/.test(within)) continue;
    found.add(within);
  }
  return [...found];
}

used by