From 79eb0c5f7e4e970bb29a4145af02a87be54c255d Mon Sep 17 00:00:00 2001 From: lauren Date: Mon, 21 Jul 2025 11:54:12 -0400 Subject: [PATCH] Update deadlinks script to take into account redirects (#7882) * Revert "Fix deadlinks (#7880)" Original commit changeset: d52b3ec73407 Revert this so we can just fix the real deadlinks. * Update deadlinks script to take into account redirects Also check if a redirect exists before erroring. --- scripts/deadLinkChecker.js | 45 +++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/scripts/deadLinkChecker.js b/scripts/deadLinkChecker.js index ab8761e26..90593b878 100644 --- a/scripts/deadLinkChecker.js +++ b/scripts/deadLinkChecker.js @@ -10,6 +10,7 @@ const PUBLIC_DIR = path.join(__dirname, '../public'); const fileCache = new Map(); const anchorMap = new Map(); // Map> const contributorMap = new Map(); // Map +const redirectMap = new Map(); // Map let errorCodes = new Set(); async function readFileWithCache(filePath) { @@ -162,6 +163,22 @@ async function validateLink(link) { return {valid: true}; } + // Check for redirects + if (redirectMap.has(urlWithoutAnchor)) { + const redirectDestination = redirectMap.get(urlWithoutAnchor); + if ( + redirectDestination.startsWith('http://') || + redirectDestination.startsWith('https://') + ) { + return {valid: true}; + } + const redirectedLink = { + ...link, + url: redirectDestination + (anchorMatch ? anchorMatch[0] : ''), + }; + return validateLink(redirectedLink); + } + // Check if it's an error code link const errorCodeMatch = urlWithoutAnchor.match(/^\/errors\/(\d+)$/); if (errorCodeMatch) { @@ -295,17 +312,42 @@ async function fetchErrorCodes() { } const codes = await response.json(); errorCodes = new Set(Object.keys(codes)); - console.log(chalk.gray(`Fetched ${errorCodes.size} React error codes\n`)); + console.log(chalk.gray(`Fetched ${errorCodes.size} React error codes`)); } catch (error) { throw new Error(`Failed to fetch error codes: ${error.message}`); } } +async function buildRedirectsMap() { + try { + const vercelConfigPath = path.join(__dirname, '../vercel.json'); + const vercelConfig = JSON.parse( + await fs.promises.readFile(vercelConfigPath, 'utf8') + ); + + if (vercelConfig.redirects) { + for (const redirect of vercelConfig.redirects) { + redirectMap.set(redirect.source, redirect.destination); + } + console.log( + chalk.gray(`Loaded ${redirectMap.size} redirects from vercel.json`) + ); + } + } catch (error) { + console.log( + chalk.yellow( + `Warning: Could not load redirects from vercel.json: ${error.message}\n` + ) + ); + } +} + async function main() { const files = getMarkdownFiles(); console.log(chalk.gray(`Checking ${files.length} markdown files...`)); await fetchErrorCodes(); + await buildRedirectsMap(); await buildContributorMap(); await buildAnchorMap(files); @@ -315,6 +357,7 @@ async function main() { const totalLinks = results.reduce((sum, r) => sum + r.totalLinks, 0); if (deadLinks.length > 0) { + console.log('\n'); for (const link of deadLinks) { console.log(chalk.yellow(`${link.file}:${link.line}:${link.column}`)); console.log(chalk.reset(` Link text: ${link.text}`));