1

Ajoute un outil de gestion interactive des liens morts

This commit is contained in:
2026-03-25 23:06:23 +01:00
parent dc262bdd97
commit 3cb735333a
7 changed files with 1115 additions and 21 deletions

View File

@@ -0,0 +1,93 @@
const test = require("node:test");
const assert = require("node:assert/strict");
const fs = require("node:fs/promises");
const os = require("node:os");
const path = require("node:path");
const { findUrlOccurrences, replaceUrlInFiles } = require("../lib/url_replacements");
const { loadExternalLinksReport, getLinksByStatus } = require("../lib/external_links_report");
/**
* Ecrit un fichier texte en creant son dossier parent.
* @param {string} filePath Chemin absolu du fichier.
* @param {string} content Contenu a ecrire.
*/
async function writeFixture(filePath, content) {
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(filePath, content, "utf8");
}
test("replaceUrlInFiles remplace les occurrences exactes dans markdown, yaml et json", async () => {
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "dead-links-replace-"));
const contentRoot = path.join(tempRoot, "content");
const deadUrl = "https://dead.example.com/page";
const replacementUrl = "https://archive.example.com/page";
await writeFixture(
path.join(contentRoot, "article", "index.md"),
`Lien [mort](${deadUrl})\nEncore ${deadUrl}\n`
);
await writeFixture(
path.join(contentRoot, "article", "data", "meta.yaml"),
`source: "${deadUrl}"\n`
);
await writeFixture(
path.join(contentRoot, "stats", "data.json"),
JSON.stringify({ url: deadUrl, untouched: "https://ok.example.com" }, null, 2)
);
const matches = await findUrlOccurrences(contentRoot, deadUrl);
assert.deepStrictEqual(
matches.map((match) => [path.relative(contentRoot, match.filePath), match.occurrences]),
[
["article/data/meta.yaml", 1],
["article/index.md", 2],
["stats/data.json", 1],
]
);
const result = await replaceUrlInFiles(contentRoot, deadUrl, replacementUrl, { matches });
assert.equal(result.totalOccurrences, 4);
assert.equal(result.changedFiles.length, 3);
const markdown = await fs.readFile(path.join(contentRoot, "article", "index.md"), "utf8");
const yaml = await fs.readFile(path.join(contentRoot, "article", "data", "meta.yaml"), "utf8");
const json = await fs.readFile(path.join(contentRoot, "stats", "data.json"), "utf8");
assert.ok(markdown.includes(replacementUrl));
assert.ok(!markdown.includes(deadUrl));
assert.ok(yaml.includes(replacementUrl));
assert.ok(json.includes(replacementUrl));
await fs.rm(tempRoot, { recursive: true, force: true });
});
test("loadExternalLinksReport retourne correctement les liens 404 du cache YAML", async () => {
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "dead-links-cli-"));
const reportPath = path.join(tempRoot, "external_links.yaml");
const deadUrl = "https://dead.example.com/article";
await writeFixture(
reportPath,
[
"generatedAt: '2026-03-25T21:00:00.000Z'",
"links:",
` - url: ${deadUrl}`,
" status: 404",
" locations:",
" - file: content/demo/index.md",
" line: 5",
" page: /demo",
"",
].join("\n")
);
const report = loadExternalLinksReport(reportPath);
const deadLinks = getLinksByStatus(report, 404);
assert.equal(report.generatedAt, "2026-03-25T21:00:00.000Z");
assert.equal(deadLinks.length, 1);
assert.equal(deadLinks[0].url, deadUrl);
assert.equal(deadLinks[0].locations[0].file, "content/demo/index.md");
assert.equal(deadLinks[0].locations[0].line, 5);
await fs.rm(tempRoot, { recursive: true, force: true });
});