import type { Parser, ExtractedRecord, RawDocument } from "@dci/connectors"; import { countryFromText, cleanText, parseMoney, slugify } from "@dci/core"; import { loadHtml, txt, resolvePlace, type Place } from "./shared.js"; import { countryFromStateName, stateDisplayName } from "./city-coords.js"; /** * Meta data centers — https://datacenters.atmeta.com/all-locations/ (WordPress, server-rendered cards): *
eyebrow = state/country, title = town, excerpt = "$1 billion+ investment | 2010 break ground | * 1,000 skilled trade workers … | 350+ operational jobs …", CTA = info-sheet PDF. * The page states investment and the break-ground year but NOT the operating status. Status is therefore: * break-ground year > current year → announced ; ≥ current year − 1 → under_construction ; older → unknown * (recorded in the description and left for reconciliation with other sources — never assumed operational). */ export const metaDatacentersParser: Parser = { name: "cloud_meta_datacenters_v1", version: "1.0.0", parse(doc: RawDocument, ctx): ExtractedRecord[] { const $ = loadHtml(doc); const out: ExtractedRecord[] = []; const year = new Date().getUTCFullYear(); const seen = new Set(); $("article.card").each((_, card) => { const title = txt($(card).find(".card__title").first()); const eyebrow = txt($(card).find(".card__eyebrow-text").first()); if (!title) return; const excerptHtml = $(card).find(".card__excerpt").first().html() ?? ""; const excerpt = cleanText(excerptHtml.replace(//gi, " | ").replace(/<[^>]+>/g, "")) ?? ""; const key = `meta:dc:${slugify(`${title}-${eyebrow}`)}`; if (seen.has(key)) return; seen.add(key); const regionIso = countryFromStateName(eyebrow); const iso = regionIso ?? countryFromText(eyebrow) ?? null; let place: Place = resolvePlace(title, iso, regionIso ? eyebrow : null); if (!place.city) place.city = title; if (!place.countryIso2) place.countryIso2 = iso; if (regionIso && !place.regionName) place.regionName = stateDisplayName(eyebrow); const breakGround = excerpt.match(/(\d{4})\s*break ground/i)?.[1] ?? null; const bgYear = breakGround ? Number(breakGround) : null; const status = bgYear == null ? "unknown" : bgYear > year ? "announced" : bgYear >= year - 1 ? "under_construction" : "unknown"; const investText = excerpt.split("|")[0]?.trim() ?? null; const money = parseMoney(investText ?? ""); const sheet = $(card).find("a[href*='info-sheet']").first().attr("href")?.trim() ?? null; const fb = $(card).find(".card__follow-us-cta").first().attr("href")?.trim() ?? null; const externalIds: Record = { meta_location: `${title}, ${eyebrow}` }; if (sheet) externalIds.meta_info_sheet = sheet; if (fb) externalIds.facebook_page = fb; if (investText) externalIds.published_investment = investText; if (money) { externalIds.investment_amount = money.amount; externalIds.investment_currency = money.currency; } out.push({ kind: "facility", key, url: doc.finalUrl, pageType: "facility_index", certainty: 0.9, methods: { name: "html:.card__title", regionName: "html:.card__eyebrow-text", constructionStartedOn: "regex:break ground", status: "heuristic:break-ground-year", lat: "curated-city-table" }, data: { name: `Meta ${title} Data Center`, operatorName: "Meta", city: place.city, regionName: place.regionName, countryIso2: place.countryIso2, lat: place.lat, lng: place.lng, geoPrecision: place.geoPrecision ?? "city", status, facilityType: "hyperscale", isHyperscale: true, constructionStartedOn: breakGround, website: sheet ?? doc.finalUrl, description: `Meta data center site (${eyebrow}). Published: ${excerpt}${status === "unknown" ? " Operating status not stated on the page." : ""}`, externalIds, }, }); }); ctx.log("info", `meta: ${out.length} data center sites`); return out; }, };