mirror of
https://github.com/byoungd/up.git
synced 2026-10-30 07:32:53 +08:00
81 lines
2.3 KiB
JavaScript
81 lines
2.3 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
import { dirname, extname, join, relative, resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
const IGNORED_DIRS = new Set([
|
|
".git",
|
|
".codex-artifact-work",
|
|
"node_modules",
|
|
"dist",
|
|
"cache",
|
|
"outputs",
|
|
"output",
|
|
"tmp",
|
|
"test-results",
|
|
"playwright-report",
|
|
]);
|
|
const markdownFiles = [];
|
|
const links = new Map();
|
|
|
|
function walk(dir) {
|
|
for (const name of readdirSync(dir)) {
|
|
if (IGNORED_DIRS.has(name) || name === ".external-links.md") continue;
|
|
const file = join(dir, name);
|
|
const stat = statSync(file);
|
|
if (stat.isDirectory()) walk(file);
|
|
else if (extname(name).toLowerCase() === ".md") markdownFiles.push(file);
|
|
}
|
|
}
|
|
|
|
function addLink(url, file, line) {
|
|
const clean = url.replace(/[.,;:!?]+$/, "");
|
|
try {
|
|
const parsed = new URL(clean);
|
|
if (!/^https?:$/.test(parsed.protocol)) return;
|
|
} catch {
|
|
return;
|
|
}
|
|
const source = `${relative(ROOT, file)}:${line}`;
|
|
const locations = links.get(clean) || [];
|
|
if (!locations.includes(source)) locations.push(source);
|
|
links.set(clean, locations);
|
|
}
|
|
|
|
function extract(file) {
|
|
const lines = readFileSync(file, "utf8").split("\n");
|
|
let inFence = false;
|
|
lines.forEach((line, index) => {
|
|
if (/^\s*(```|~~~)/.test(line)) {
|
|
inFence = !inFence;
|
|
return;
|
|
}
|
|
if (inFence) return;
|
|
|
|
const lineNumber = index + 1;
|
|
const markdownTargets = line.matchAll(/\]\((https?:\/\/[^)\s]+)(?:\s+["'][^)]*)?\)/gi);
|
|
for (const match of markdownTargets) addLink(match[1], file, lineNumber);
|
|
|
|
const htmlTargets = line.matchAll(/\b(?:href|src)=(["'])(https?:\/\/[^"']+)\1/gi);
|
|
for (const match of htmlTargets) addLink(match[2], file, lineNumber);
|
|
|
|
const bareTargets = line.matchAll(/https?:\/\/[^\s<>"'()]+/gi);
|
|
for (const match of bareTargets) addLink(match[0], file, lineNumber);
|
|
});
|
|
}
|
|
|
|
if (!existsSync(join(ROOT, "docs"))) {
|
|
console.error("docs directory not found");
|
|
process.exit(1);
|
|
}
|
|
|
|
walk(ROOT);
|
|
for (const file of markdownFiles) extract(file);
|
|
|
|
console.log("# External links extracted from repository Markdown\n");
|
|
for (const [url, locations] of links) {
|
|
console.log(`- [${locations.join(", ")}](${url})`);
|
|
}
|