#!/usr/bin/env node import fs from "node:fs/promises"; import path from "node:path"; import process from "node:process"; const CWD = process.cwd(); const DIST_ROOT = path.join(CWD, "dist"); const ALIASES_PATH = path.join(CWD, "src", "anchors", "anchor-aliases.json"); const argv = process.argv.slice(2); const DRY_RUN = argv.includes("--dry-run"); const STRICT = argv.includes("--strict"); function escRe(s) { return String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } function normalizeRoute(route) { let r = String(route || "").trim(); if (!r.startsWith("/")) r = "/" + r; if (!r.endsWith("/")) r = r + "/"; r = r.replace(/\/{2,}/g, "/"); return r; } async function exists(p) { try { await fs.access(p); return true; } catch { return false; } } function hasId(html, id) { const re = new RegExp(`\\bid=(["'])${escRe(id)}\\1`, "i"); return re.test(html); } function injectBeforeId(html, newId, injectHtml) { // insère juste avant la balise qui porte id="newId" const re = new RegExp( `(<[^>]+\\bid=(["'])${escRe(newId)}\\2[^>]*>)`, "i" ); const m = html.match(re); if (!m || m.index == null) return { html, injected: false }; const i = m.index; const out = html.slice(0, i) + injectHtml + "\n" + html.slice(i); return { html: out, injected: true }; } async function main() { if (!(await exists(ALIASES_PATH))) { console.log("ℹ️ Aucun fichier d'aliases (src/anchors/anchor-aliases.json). Skip."); return; } const raw = await fs.readFile(ALIASES_PATH, "utf-8"); /** @type {Record>} */ const aliases = JSON.parse(raw); const routes = Object.keys(aliases || {}); if (routes.length === 0) { console.log("ℹ️ Aliases vides. Rien à injecter."); return; } let changedFiles = 0; let injectedCount = 0; let warnCount = 0; for (const routeKey of routes) { const route = normalizeRoute(routeKey); const map = aliases[routeKey] || {}; const entries = Object.entries(map); if (entries.length === 0) continue; const rel = route.replace(/^\/+|\/+$/g, ""); // sans slash const htmlPath = path.join(DIST_ROOT, rel, "index.html"); if (!(await exists(htmlPath))) { const msg = `⚠️ dist introuvable pour route=${route} (${htmlPath})`; if (STRICT) throw new Error(msg); console.log(msg); warnCount++; continue; } let html = await fs.readFile(htmlPath, "utf-8"); let fileChanged = false; for (const [oldId, newId] of entries) { if (!oldId || !newId) continue; if (hasId(html, oldId)) { // alias déjà présent → idempotent continue; } if (!hasId(html, newId)) { const msg = `⚠️ newId introuvable: ${route} old=${oldId} -> new=${newId}`; if (STRICT) throw new Error(msg); console.log(msg); warnCount++; continue; } const aliasSpan = ``; const r = injectBeforeId(html, newId, aliasSpan); if (!r.injected) { const msg = `⚠️ injection impossible (pattern non trouvé) : ${route} new=${newId}`; if (STRICT) throw new Error(msg); console.log(msg); warnCount++; continue; } html = r.html; fileChanged = true; injectedCount++; } if (fileChanged) { changedFiles++; if (!DRY_RUN) await fs.writeFile(htmlPath, html, "utf-8"); } } console.log( `✅ inject-anchor-aliases: files_changed=${changedFiles} aliases_injected=${injectedCount} warnings=${warnCount}` + (DRY_RUN ? " (dry-run)" : "") ); if (STRICT && warnCount > 0) process.exit(2); } main().catch((e) => { console.error("💥 inject-anchor-aliases:", e?.message || e); process.exit(1); });