SPB Git forge
38commits 1branches 0releases
338.7 MBsize
maindefault branch
4 h agolast push
HTML 53.9% TypeScript 44.5% JavaScript 0.6% SQL 0.5%
4.2 KB · 79 lines typescript
Raw Blame History
1import type { Parser, ExtractedRecord, RawDocument } from "@dci/connectors";2import { countryFromText, cleanText, parseMoney, slugify } from "@dci/core";3import { loadHtml, txt, resolvePlace, type Place } from "./shared.js";4import { countryFromStateName, stateDisplayName } from "./city-coords.js";56/**7 * Meta data centers — https://datacenters.atmeta.com/all-locations/ (WordPress, server-rendered cards):8 *   <article class="card …"> eyebrow = state/country, title = town, excerpt = "$1 billion+ investment | 2010 break ground |9 *   1,000 skilled trade workers … | 350+ operational jobs …", CTA = info-sheet PDF.10 * The page states investment and the break-ground year but NOT the operating status. Status is therefore:11 *   break-ground year > current year → announced ; ≥ current year − 1 → under_construction ; older → unknown12 * (recorded in the description and left for reconciliation with other sources — never assumed operational).13 */14export const metaDatacentersParser: Parser = {15  name: "cloud_meta_datacenters_v1",16  version: "1.0.0",17  parse(doc: RawDocument, ctx): ExtractedRecord[] {18    const $ = loadHtml(doc);19    const out: ExtractedRecord[] = [];20    const year = new Date().getUTCFullYear();21    const seen = new Set<string>();22    $("article.card").each((_, card) => {23      const title = txt($(card).find(".card__title").first());24      const eyebrow = txt($(card).find(".card__eyebrow-text").first());25      if (!title) return;26      const excerptHtml = $(card).find(".card__excerpt").first().html() ?? "";27      const excerpt = cleanText(excerptHtml.replace(/<br\s*\/?>/gi, " | ").replace(/<[^>]+>/g, "")) ?? "";28      const key = `meta:dc:${slugify(`${title}-${eyebrow}`)}`;29      if (seen.has(key)) return;30      seen.add(key);31      const regionIso = countryFromStateName(eyebrow);32      const iso = regionIso ?? countryFromText(eyebrow) ?? null;33      let place: Place = resolvePlace(title, iso, regionIso ? eyebrow : null);34      if (!place.city) place.city = title;35      if (!place.countryIso2) place.countryIso2 = iso;36      if (regionIso && !place.regionName) place.regionName = stateDisplayName(eyebrow);37      const breakGround = excerpt.match(/(\d{4})\s*break ground/i)?.[1] ?? null;38      const bgYear = breakGround ? Number(breakGround) : null;39      const status = bgYear == null ? "unknown" : bgYear > year ? "announced" : bgYear >= year - 1 ? "under_construction" : "unknown";40      const investText = excerpt.split("|")[0]?.trim() ?? null;41      const money = parseMoney(investText ?? "");42      const sheet = $(card).find("a[href*='info-sheet']").first().attr("href")?.trim() ?? null;43      const fb = $(card).find(".card__follow-us-cta").first().attr("href")?.trim() ?? null;44      const externalIds: Record<string, string | number> = { meta_location: `${title}, ${eyebrow}` };45      if (sheet) externalIds.meta_info_sheet = sheet;46      if (fb) externalIds.facebook_page = fb;47      if (investText) externalIds.published_investment = investText;48      if (money) { externalIds.investment_amount = money.amount; externalIds.investment_currency = money.currency; }49      out.push({50        kind: "facility",51        key,52        url: doc.finalUrl,53        pageType: "facility_index",54        certainty: 0.9,55        methods: { name: "html:.card__title", regionName: "html:.card__eyebrow-text", constructionStartedOn: "regex:break ground", status: "heuristic:break-ground-year", lat: "curated-city-table" },56        data: {57          name: `Meta ${title} Data Center`,58          operatorName: "Meta",59          city: place.city,60          regionName: place.regionName,61          countryIso2: place.countryIso2,62          lat: place.lat,63          lng: place.lng,64          geoPrecision: place.geoPrecision ?? "city",65          status,66          facilityType: "hyperscale",67          isHyperscale: true,68          constructionStartedOn: breakGround,69          website: sheet ?? doc.finalUrl,70          description: `Meta data center site (${eyebrow}). Published: ${excerpt}${status === "unknown" ? " Operating status not stated on the page." : ""}`,71          externalIds,72        },73      });74    });75    ctx.log("info", `meta: ${out.length} data center sites`);76    return out;77  },78};79