Report links to missing pages as broken in the link checker (#63946) · github/docs@f1e0457 · GitHub
Skip to content

Commit f1e0457

Browse files
heiskrCopilot
andauthored
Report links to missing pages as broken in the link checker (#63946)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bf5940de-658e-4a1a-bffa-3018fb78b138
1 parent b8921b0 commit f1e0457

3 files changed

Lines changed: 94 additions & 8 deletions

File tree

‎content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/troubleshooting-resource-allocation-problems.md‎

Lines changed: 1 addition & 1 deletion

‎src/links/lib/extract-links.ts‎

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { latestStable } from '@/versions/lib/enterprise-server-releases'
77
import removeFPTFromPath from '@/versions/lib/remove-fpt-from-path'
88
import { getDataByLanguage } from '@/data-directory/lib/get-data'
99
import getRedirect from '@/redirects/lib/get-redirect'
10+
import { applyGraphqlCategoryRedirect } from '@/redirects/lib/graphql-category-redirect'
1011
import { isArchivedVersionByPath } from '@/archives/lib/is-archived-version'
1112
import { getFeaturesByVersion } from '@/versions/middleware/features'
1213
import type { Context, Page } from '@/types'
@@ -473,6 +474,28 @@ function versionedPageKey(
473474
return { key: `/${language}${withoutLanguage}`, withoutLanguage }
474475
}
475476

477+
// getRedirect guesses some destinations without checking pages, and the redirect middleware
478+
// skips those when the page is missing, so an unverified target is a broken link.
479+
function redirectTargetExists(
480+
redirect: string,
481+
redirectTarget: string,
482+
pageMap: Record<string, Page>,
483+
language: string,
484+
): boolean {
485+
if (redirect.includes('://')) return true
486+
// The redirect middleware looks up pages without query strings, fragments, or .md.
487+
const toLookupKey = (uri: string) => uri.split(/[?#]/)[0].replace(/\.md$/, '')
488+
const target = toLookupKey(redirectTarget)
489+
// Non-English runs load only their own pages, so the target must exist in the checked language.
490+
const localizedTarget = target === '/' ? `/${language}` : `/${language}${target}`
491+
return Boolean(
492+
pageMap[toLookupKey(redirect)] ||
493+
pageMap[target] ||
494+
pageMap[localizedTarget] ||
495+
isArchivedVersionByPath(redirect).isArchived,
496+
)
497+
}
498+
476499
// Resolve versionless links inside the checked version first, matching runtime rendering.
477500
// Without version context, non-FPT versionless links can fall through to fallback redirects
478501
// and look like links that need updating.
@@ -572,13 +595,14 @@ export function checkInternalLink(
572595
userLanguage: 'en',
573596
pages: pageMap,
574597
}
575-
const redirect = getRedirect(withEn, context as unknown as Context)
576-
if (redirect) {
598+
const resolvedRedirect = getRedirect(withEn, context as unknown as Context)
599+
if (resolvedRedirect) {
600+
// The redirect middleware rewrites legacy GraphQL kind URLs before it checks the target.
601+
const redirect = applyGraphqlCategoryRedirect(resolvedRedirect) || resolvedRedirect
577602
// Strip locale prefixes to match flat-map branches; a bare locale root normalizes to /.
578-
return {
579-
exists: true,
580-
isRedirect: true,
581-
redirectTarget: redirect.replace(/^\/[a-z]{2}(-[a-z]{2})?(?=\/|$)/, '') || '/',
603+
const redirectTarget = redirect.replace(/^\/[a-z]{2}(-[a-z]{2})?(?=\/|$)/, '') || '/'
604+
if (redirectTargetExists(redirect, redirectTarget, pageMap, language)) {
605+
return { exists: true, isRedirect: true, redirectTarget }
582606
}
583607
}
584608
} catch {

‎src/links/tests/extract-links.ts‎

Lines changed: 63 additions & 1 deletion

0 commit comments

Comments
 (0)