diff --git a/package.json b/package.json index 0f990e5..2be8357 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "build": "astro build", "preview": "astro preview", "astro": "astro", - "postbuild": "npx pagefind --site dist", + "postbuild": "node scripts/inject-anchor-aliases.mjs && npx pagefind --site dist", "import": "node scripts/import-docx.mjs", "apply:ticket": "node scripts/apply-ticket.mjs", "test": "npm run build && npm run test:anchors && node scripts/check-inline-js.mjs", diff --git a/scripts/check-anchors.mjs b/scripts/check-anchors.mjs index 90578e8..1d003be 100755 --- a/scripts/check-anchors.mjs +++ b/scripts/check-anchors.mjs @@ -29,14 +29,29 @@ async function walk(dir) { return out; } -// Contrat : .reading p[id^="p-"] +// Contrat : +// - paragraphes citables : .reading p[id^="p-"] +// - alias web-natifs : .reading span.para-alias[id^="p-"] function extractIds(html) { if (!html.includes('class="reading"')) return []; - const ids = []; - const re = /
]*\sid="(p-[^"]+)"/g; - let m; - while ((m = re.exec(html))) ids.push(m[1]); + const ids = []; + let m; + + // 1) IDs principaux (paragraphes) + const reP = /
]*\sid="(p-[^"]+)"/g;
+ while ((m = reP.exec(html))) ids.push(m[1]);
+
+ // 2) IDs alias (spans injectés)
+ // cas A : id="..." avant class="...para-alias..."
+ const reA1 = /]*\bid="(p-[^"]+)"[^>]*\bclass="[^"]*\bpara-alias\b[^"]*"/g;
+ while ((m = reA1.exec(html))) ids.push(m[1]);
+
+ // cas B : class="...para-alias..." avant id="..."
+ const reA2 = /]*\bclass="[^"]*\bpara-alias\b[^"]*"[^>]*\bid="(p-[^"]+)"/g;
+ while ((m = reA2.exec(html))) ids.push(m[1]);
+
+ // Dé-doublonnage (on garde un ordre stable)
const seen = new Set();
const uniq = [];
for (const id of ids) {
diff --git a/scripts/inject-anchor-aliases.mjs b/scripts/inject-anchor-aliases.mjs
new file mode 100644
index 0000000..b1d5657
--- /dev/null
+++ b/scripts/inject-anchor-aliases.mjs
@@ -0,0 +1,143 @@
+#!/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