Files
LangBot/scripts/normalize-static-paths.mjs
T

57 lines
1.7 KiB
JavaScript

#!/usr/bin/env node
import { access, readdir, rename } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
function decodePathSegment(segment) {
const decoded = decodeURIComponent(segment);
if (decoded.includes("/") || decoded.includes("\\") || decoded.includes("\0")) {
throw new Error(`Refusing to decode unsafe output path segment: ${segment}`);
}
return decoded;
}
async function exists(target) {
try {
await access(target);
return true;
} catch {
return false;
}
}
export async function normalizeStaticPaths(directory) {
let renamed = 0;
for (const entry of await readdir(directory, { withFileTypes: true })) {
const source = path.join(directory, entry.name);
if (entry.isDirectory()) renamed += await normalizeStaticPaths(source);
const decodedName = decodePathSegment(entry.name);
if (decodedName === entry.name) continue;
const destination = path.join(directory, decodedName);
if (await exists(destination)) {
throw new Error(`Static output path collision: ${source} -> ${destination}`);
}
await rename(source, destination);
renamed += 1;
}
return renamed;
}
async function main() {
const publicRoot = path.join(ROOT, "dist/public");
const renamed = await normalizeStaticPaths(publicRoot);
console.log(`Normalized ${renamed} percent-encoded static path segments`);
}
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
main().catch((error) => {
console.error(error.stack || error.message);
process.exitCode = 1;
});
}