familyfedie-website/scripts/organize-content.mjs
Loyyd 8e40b8599b
Some checks are pending
/ deploy (push) Waiting to run
organized
2026-06-11 08:17:11 +02:00

147 lines
4.1 KiB
JavaScript

import fs from "node:fs";
import path from "node:path";
const repoRoot = process.cwd();
const websiteRoot = path.join(repoRoot, "website");
function listTopLevelHtml() {
return fs
.readdirSync(websiteRoot, { withFileTypes: true })
.filter((entry) => entry.isFile() && entry.name.endsWith(".html"))
.map((entry) => entry.name);
}
function destinationFor(filename) {
if (filename === "archive-speeches.html") {
return "speeches/index.html";
}
let match = filename.match(/^archive-speeches-rev-dr-sun-myung-moon-(.+)\.html$/);
if (match) {
return `speeches/rev-dr-sun-myung-moon/${match[1]}.html`;
}
match = filename.match(/^blog-category-speeches-of-rev-sun-myung-moon(?:-(.+))?\.html$/);
if (match) {
return `speeches/categories/rev-sun-myung-moon${match[1] ? `-${match[1]}` : ""}.html`;
}
if (filename === "blog-category-speeches-of-mrs-hak-ja-han-moon.html") {
return "speeches/categories/mrs-hak-ja-han-moon.html";
}
match = filename.match(/^blog-category-(.+)\.html$/);
if (match) {
return `blog/categories/${match[1]}.html`;
}
match = filename.match(/^blog-tag-(.+)\.html$/);
if (match) {
return `blog/tags/${match[1]}.html`;
}
match = filename.match(/^blog-(\d{4})-(\d{2})-(\d{2})-(.+)\.html$/);
if (match) {
return `blog/${match[1]}/${match[2]}/${match[3]}/${match[4]}.html`;
}
return null;
}
function splitUrl(value) {
const match = value.match(/^([^?#]*)([?#].*)?$/);
return { pathname: match?.[1] ?? value, suffix: match?.[2] ?? "" };
}
function stripLeadingParentDirs(value) {
return value.replace(/^(\.\/)?/, "").replace(/^(\.\.\/)+/, "");
}
function shouldSkipUrl(value) {
return (
value === "" ||
value.startsWith("#") ||
value.startsWith("/") ||
/^[a-z][a-z0-9+.-]*:/i.test(value)
);
}
function rewriteUrl(value, movedPaths) {
if (shouldSkipUrl(value)) {
return value;
}
const { pathname, suffix } = splitUrl(value);
const normalized = stripLeadingParentDirs(pathname);
if (normalized.startsWith("assets/") || normalized.startsWith("css/") || normalized.startsWith("js/")) {
return `/${normalized}${suffix}`;
}
if (movedPaths.has(normalized)) {
return `/${movedPaths.get(normalized)}${suffix}`;
}
if (normalized.endsWith(".html")) {
return `/${normalized}${suffix}`;
}
return value;
}
function rewriteHtml(html, movedPaths) {
let output = html.replace(
/\b(href|src|action)=(["'])([^"']+)\2/g,
(_match, attr, quote, value) => `${attr}=${quote}${rewriteUrl(value, movedPaths)}${quote}`
);
output = output.replace(/\burl\((["']?)(\.\.\/(?:assets|css|js)\/[^"')]+)\1\)/g, (_match, quote, value) => {
const rewritten = rewriteUrl(value, movedPaths);
return `url(${quote}${rewritten}${quote})`;
});
return output;
}
const movedPaths = new Map();
for (const filename of listTopLevelHtml()) {
const destination = destinationFor(filename);
if (destination) {
movedPaths.set(filename, destination);
}
}
for (const [filename, destination] of movedPaths) {
const sourcePath = path.join(websiteRoot, filename);
const destinationPath = path.join(websiteRoot, destination);
fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
fs.renameSync(sourcePath, destinationPath);
}
function walk(dir) {
return fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
return walk(fullPath);
}
return fullPath.endsWith(".html") || fullPath.endsWith(".md") ? [fullPath] : [];
});
}
let rewritten = 0;
for (const filePath of [...walk(websiteRoot), path.join(repoRoot, "README.md"), path.join(repoRoot, "docs", "SITE-STRUCTURE.md")]) {
if (!fs.existsSync(filePath)) {
continue;
}
const original = fs.readFileSync(filePath, "utf8");
const updated = rewriteHtml(original, movedPaths);
if (updated !== original) {
fs.writeFileSync(filePath, updated);
rewritten += 1;
}
}
console.log(`Moved ${movedPaths.size} HTML files into blog/ and speeches/.`);
console.log(`Rewrote links in ${rewritten} files.`);