build: fix astro mdx/rehype config + dedupe duplicate ids in dist
This commit is contained in:
134
scripts/dedupe-ids-dist.mjs
Normal file
134
scripts/dedupe-ids-dist.mjs
Normal file
@@ -0,0 +1,134 @@
|
||||
import { promises as fs } from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const DIST_DIR = path.resolve("dist");
|
||||
|
||||
/** @param {string} dir */
|
||||
async function walkHtml(dir) {
|
||||
/** @type {string[]} */
|
||||
const out = [];
|
||||
const entries = await fs.readdir(dir, { withFileTypes: true });
|
||||
for (const e of entries) {
|
||||
const p = path.join(dir, e.name);
|
||||
if (e.isDirectory()) out.push(...(await walkHtml(p)));
|
||||
else if (e.isFile() && p.endsWith(".html")) out.push(p);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** @param {string} attrs */
|
||||
function getClass(attrs) {
|
||||
const m = attrs.match(/\bclass="([^"]*)"/i);
|
||||
return m ? m[1] : "";
|
||||
}
|
||||
|
||||
/** @param {{tag:string,id:string,cls:string}} occ */
|
||||
function score(occ) {
|
||||
// plus petit = mieux (on garde)
|
||||
if (occ.tag === "span" && /\bdetails-anchor\b/.test(occ.cls)) return 0;
|
||||
if (/^h[1-6]$/.test(occ.tag)) return 1;
|
||||
if (occ.tag === "p" && occ.id.startsWith("p-")) return 2;
|
||||
return 10; // tout le reste (toc, nav, etc.)
|
||||
}
|
||||
|
||||
async function main() {
|
||||
let changedFiles = 0;
|
||||
let removed = 0;
|
||||
|
||||
const files = await walkHtml(DIST_DIR);
|
||||
|
||||
for (const file of files) {
|
||||
let html = await fs.readFile(file, "utf8");
|
||||
|
||||
// capture: <tag ... id="X" ...>
|
||||
const re = /<([A-Za-z][\w:-]*)([^>]*?)\s+id="([^"]+)"([^>]*?)>/g;
|
||||
|
||||
/** @type {Array<{id:string,tag:string,pre:string,post:string,start:number,end:number,cls:string,idx:number}>} */
|
||||
const occs = [];
|
||||
let m;
|
||||
let idx = 0;
|
||||
|
||||
while ((m = re.exec(html)) !== null) {
|
||||
const tag = m[1].toLowerCase();
|
||||
const pre = m[2] || "";
|
||||
const id = m[3] || "";
|
||||
const post = m[4] || "";
|
||||
const fullAttrs = `${pre}${post}`;
|
||||
const cls = getClass(fullAttrs);
|
||||
|
||||
occs.push({
|
||||
id,
|
||||
tag,
|
||||
pre,
|
||||
post,
|
||||
start: m.index,
|
||||
end: m.index + m[0].length,
|
||||
cls,
|
||||
idx: idx++,
|
||||
});
|
||||
}
|
||||
|
||||
if (occs.length === 0) continue;
|
||||
|
||||
/** @type {Map<string, Array<typeof occs[number]>>} */
|
||||
const byId = new Map();
|
||||
for (const o of occs) {
|
||||
if (!o.id) continue;
|
||||
const arr = byId.get(o.id) || [];
|
||||
arr.push(o);
|
||||
byId.set(o.id, arr);
|
||||
}
|
||||
|
||||
/** @type {Array<{start:number,end:number,repl:string}>} */
|
||||
const edits = [];
|
||||
|
||||
for (const [id, arr] of byId.entries()) {
|
||||
if (arr.length <= 1) continue;
|
||||
|
||||
// choisir le “meilleur” porteur d’id : details-anchor > h2/h3... > p-... > reste
|
||||
const sorted = [...arr].sort((a, b) => {
|
||||
const sa = score(a);
|
||||
const sb = score(b);
|
||||
if (sa !== sb) return sa - sb;
|
||||
return a.idx - b.idx; // stable: premier
|
||||
});
|
||||
|
||||
const keep = sorted[0];
|
||||
|
||||
for (const o of sorted.slice(1)) {
|
||||
// remplacer l’ouverture de tag en supprimant l’attribut id
|
||||
// <tag{pre} id="X"{post}> ==> <tag{pre}{post}>
|
||||
const repl = `<${o.tag}${o.pre}${o.post}>`;
|
||||
edits.push({ start: o.start, end: o.end, repl });
|
||||
removed++;
|
||||
}
|
||||
|
||||
// sécurité: on “force” l'id sur le keep (au cas où il aurait été modifié plus haut)
|
||||
// (on ne touche pas au keep ici, juste on ne le retire pas)
|
||||
void keep;
|
||||
void id;
|
||||
}
|
||||
|
||||
if (edits.length === 0) continue;
|
||||
|
||||
// appliquer de la fin vers le début
|
||||
edits.sort((a, b) => b.start - a.start);
|
||||
for (const e of edits) {
|
||||
html = html.slice(0, e.start) + e.repl + html.slice(e.end);
|
||||
}
|
||||
|
||||
await fs.writeFile(file, html, "utf8");
|
||||
changedFiles++;
|
||||
}
|
||||
|
||||
if (changedFiles > 0) {
|
||||
console.log(`✅ dedupe-ids-dist: files_changed=${changedFiles} ids_removed=${removed}`);
|
||||
} else {
|
||||
console.log("ℹ️ dedupe-ids-dist: no duplicates found");
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error("❌ dedupe-ids-dist failed:", err);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user