AbdulElahGwaith's picture
Upload folder using huggingface_hub
88df9e4 verified
import { languageKeys } from '@/languages/lib/languages-server'
import nonEnterpriseDefaultVersion from '@/versions/lib/non-enterprise-default-version'
import { allVersions } from '@/versions/lib/all-versions'
import {
latest,
latestStable,
supported,
deprecatedWithFunctionalRedirects,
} from '@/versions/lib/enterprise-server-releases'
import { getPathWithLanguage, getVersionStringFromPath } from '@/frame/lib/path-utils'
import type { Context } from '@/types'
const languagePrefixRegex = new RegExp(`^/(${languageKeys.join('|')})/`)
const nonEnterpriseDefaultVersionPrefix = `/${nonEnterpriseDefaultVersion}`
const supportedAndRecentlyDeprecated = [...supported, ...deprecatedWithFunctionalRedirects]
export function splitPathByLanguage(uri: string, userLanguage?: string): [string, string] {
let language = userLanguage || 'en'
let withoutLanguage = uri
if (languagePrefixRegex.test(uri)) {
const match = uri.match(languagePrefixRegex)
if (match) {
language = match[1]
withoutLanguage = uri.replace(languagePrefixRegex, '/')
}
}
return [language, withoutLanguage]
}
// Return the new URI if there is one, otherwise return undefined.
export default function getRedirect(uri: string, context: Context): string | undefined {
const { redirects, userLanguage } = context
if (!redirects) {
return undefined
}
const [language, withoutLanguage] = splitPathByLanguage(uri, userLanguage)
if (withoutLanguage.startsWith('/github-ae@latest')) {
// It has a different business logic that the rest because it's a
// version that now will always redirect. Just a question of where to
// exactly.
const nonAERedirect = githubAERedirect(uri, context)
if (nonAERedirect.includes('/github-ae@latest')) {
// If this happened some redirect in there didn't completely
// get away from github-ae.
throw new Error('Still going to github-ae@latest URL')
}
return nonAERedirect
}
let destination: string | undefined
// `redirects` is sourced from more than one thing. The primary use
// case is gathering up the `redirect_from` frontmatter key.
// But we also have `developer.json` which contains legacy redirects.
// For example, the `developer.json` will have entries such
// `/enterprise/v4/enum/auditlogorderfield` which clearly is using
// the old formatting of the version. So to leverage the redirects
// from `developer.json` we'll look at it right away.
if (withoutLanguage in redirects) {
// But only inject the language if it's NOT an external redirect
if (redirects[withoutLanguage].includes('://')) {
return redirects[withoutLanguage]
}
return getPathWithLanguage(redirects[withoutLanguage], language)
}
let basicCorrection: string | undefined
if (withoutLanguage.startsWith(nonEnterpriseDefaultVersionPrefix)) {
// E.g. '/free-pro-team@latest/foo/bar' or '/free-pro-team@latest'
basicCorrection = `/${language}${withoutLanguage.replace(nonEnterpriseDefaultVersionPrefix, '')}`
} else if (withoutLanguage.replace('/', '') in allVersions && !languagePrefixRegex.test(uri)) {
// E.g. just '/github-ae@latest' or '/enterprise-cloud@latest'
basicCorrection = `/${language}${withoutLanguage}`
return basicCorrection
}
if (
withoutLanguage === '/enterprise-server' ||
withoutLanguage.startsWith('/enterprise-server/')
) {
// E.g. '/enterprise-server' or '/enterprise-server/3.0/foo'
basicCorrection = `/${language}${withoutLanguage.replace(
'/enterprise-server',
`/enterprise-server@${latestStable}`,
)}`
// If it's now just the version, without anything after, exit here
if (withoutLanguage === '/enterprise-server') {
return basicCorrection
}
} else if (withoutLanguage.startsWith('/enterprise-server@latest')) {
// E.g. '/enterprise-server@latest' or '/enterprise-server@latest/3.3/foo'
basicCorrection = `/${language}${withoutLanguage.replace(
'/enterprise-server@latest',
`/enterprise-server@${latestStable}`,
)}`
// If it was *just* '/enterprise-server@latest' all that's needed is
// the language but with 'latest' replaced with the value of `latest`
if (withoutLanguage === '/enterprise-server@latest') {
return basicCorrection
}
} else if (
withoutLanguage.startsWith('/enterprise/') &&
supportedAndRecentlyDeprecated.includes(withoutLanguage.split('/')[2])
) {
// E.g. '/enterprise/3.3' or '/enterprise/3.3/foo' or '/enterprise/3.0/foo
// If the URL is without a language, and no redirect is necessary,
// but it has as version prefix, the language has to be there
// otherwise it will never be found in `req.context.pages`
const version = withoutLanguage.split('/')[2]
if (withoutLanguage === `/enterprise/${version}`) {
// E.g. `/enterprise/3.0`
basicCorrection = `/${language}${withoutLanguage.replace(
`/enterprise/${version}`,
`/enterprise-server@${version}`,
)}`
return basicCorrection
} else {
basicCorrection = `/${language}${withoutLanguage.replace(
`/enterprise/${version}/`,
`/enterprise-server@${version}/`,
)}`
}
} else if (withoutLanguage === '/enterprise') {
// E.g. `/enterprise` exactly
basicCorrection = `/${language}/enterprise-server@${latest}`
return basicCorrection
} else if (
withoutLanguage.startsWith('/enterprise/') &&
!supported.includes(withoutLanguage.split('/')[2])
) {
// E.g. '/en/enterprise/user/github/foo'
// If the URL is without a language, and no redirect is necessary,
// but it has as version prefix, the language has to be there
// otherwise it will never be found in `req.context.pages`
basicCorrection = `/${language}${withoutLanguage
.replace(`/enterprise/`, `/enterprise-server@${latest}/`)
.replace('/user/', '/')}`
} else if (withoutLanguage.startsWith('/insights')) {
// E.g. '/insights/foo'
basicCorrection = uri.replace('/insights', `${language}/enterprise-server@${latest}/insights`)
}
if (basicCorrection) {
return getRedirect(basicCorrection, context) || basicCorrection
}
if (withoutLanguage.startsWith('/admin/')) {
const prefix = `/enterprise-server@${latest}`
let suffix = withoutLanguage
if (suffix.startsWith('/admin/guides/')) {
suffix = suffix.replace('/admin/guides/', '/admin/')
}
const newURL = prefix + suffix
destination = redirects[newURL] || newURL
} else if (
withoutLanguage.split('/')[1].includes('@') &&
withoutLanguage.split('/')[1] in allVersions
) {
// E.g. '/enterprise-server@latest' or '/github-ae@latest' or '/enterprise-server@3.3'
const majorVersion = withoutLanguage.split('/')[1].split('@')[0]
const split = withoutLanguage.split('/')
const version = split[1].split('@')[1]
let prefix: string
let suffix: string
if (supported.includes(version) || version === 'latest') {
prefix = `/${majorVersion}@${version}`
suffix = `/${split.slice(2).join('/')}`
if (
suffix.includes('/user') ||
suffix.startsWith('/admin/guide') ||
suffix.startsWith('/articles/user')
) {
suffix = tryReplacements(prefix, suffix, context) || suffix
}
} else {
// If version is not supported, we still need to set these values
prefix = `/${majorVersion}@${version}`
suffix = `/${split.slice(2).join('/')}`
}
const newURL = prefix + suffix
if (newURL !== withoutLanguage) {
// At least the prefix changed!
destination = redirects[newURL] || newURL
} else {
destination = redirects[newURL]
}
} else if (withoutLanguage.startsWith('/desktop/guides/')) {
// E.g. /desktop/guides/contributing-and-collaborat
const newURL = withoutLanguage.replace('/desktop/guides/', '/desktop/')
destination = redirects[newURL] || newURL
} else {
destination = redirects[withoutLanguage]
}
if (destination !== undefined) {
// There's hope! Now we just need to attach the correct language
// to the destination URL.
return `/${language}${destination}`
}
return undefined
}
function githubAERedirect(uri: string, context: Context): string {
const { redirects, userLanguage, pages } = context
if (!redirects || !pages) {
// Fallback to home page if context is incomplete
const [language] = splitPathByLanguage(uri, userLanguage)
return `/${language}`
}
const [language, withoutLanguage] = splitPathByLanguage(uri, userLanguage)
// From now on, github-ae@latest redirects to enterprise-cloud or
// fpt or the home page.
const cloudEquivalent = uri.replace('/github-ae@latest', '/enterprise-cloud@latest')
const fptEquivalent = uri.replace('/github-ae@latest', '')
const withoutVersion = withoutLanguage.replace('/github-ae@latest', '')
if (!withoutVersion) {
// That means the version home page.
// Don't even need to check if that exists.
// But if it was without language, inject the language as
// we go to the enterprise-cloud equivalent
if (uri.startsWith('/github-ae@latest')) {
return `/${language}${cloudEquivalent}`
}
return cloudEquivalent
}
// What if the only missing thing is a language prefix, then
// it's easy too.
if (uri.startsWith('/github-ae@latest')) {
const languageCloudEquivalent = `/${language}${cloudEquivalent}`
if (languageCloudEquivalent in pages) {
return languageCloudEquivalent
}
const languageFptEquivalent = `/${language}${fptEquivalent}`
if (languageFptEquivalent in pages) {
return languageFptEquivalent
}
} else {
// If you're here it means the URL did start with a language.
if (cloudEquivalent in pages) {
return cloudEquivalent
}
if (fptEquivalent in pages) {
return fptEquivalent
}
}
// There are redirect exceptions the specifically spell out github-ae
// in the redirect.
const legacyRedirect = redirects[withoutLanguage]
if (legacyRedirect && !legacyRedirect.includes('/github-ae@latest')) {
if (legacyRedirect.includes('://')) {
return legacyRedirect
}
return `/${language}${legacyRedirect}`
}
// The `redirects` are "pure" and don't specific a specific version.
// For example `/articles/stuff` to `/get-started/new/name`
// We look for those and try enterprise-cloud in it.
if (redirects[withoutVersion]) {
const cloudCandidate = `/${language}/enterprise-cloud@latest${redirects[withoutVersion]}`
if (cloudCandidate in pages) {
return cloudCandidate
}
const fptCandidate = `/${language}${redirects[withoutVersion]}`
// The lookup of redirects might yield a versioned URL, whose version
// might be github-ae or enterprise-server. Skip those.
if (fptCandidate in pages) {
const versionFromCandidate = getVersionStringFromPath(fptCandidate)
if (
!(
versionFromCandidate.startsWith('enterprise-server@') ||
versionFromCandidate === 'github-ae@latest'
)
) {
return fptCandidate
}
}
}
// Note that this includes completely unknown pages
return `/${language}`
}
// Over time, we've developed multiple ambiguous patterns of URLs
// You can't simply assume that all `/admin/guides` should become
// `/admin` for example.
// This function tries different string replacement on the suffix
// (the pathname after the language and version part) until it
// finds one string replacement that yields either a page or a redirect.
function tryReplacements(prefix: string, suffix: string, context: Context): string | undefined {
const { pages, redirects } = context
if (!pages || !redirects) {
return undefined
}
const test = (testSuffix: string): boolean => {
// This is a generally broad search and replace and this particular
// replacement has never been present in api documentation only enterprise
// admin documentation, so we're excluding the REST api pages
if (testSuffix.includes('/rest')) {
return false
}
const candidateAsRedirect = prefix + testSuffix
const candidateAsURL = `/en${candidateAsRedirect}`
return candidateAsRedirect in redirects || candidateAsURL in pages
}
let attempt = suffix.replace('/user', '/github')
if (test(attempt)) return attempt
attempt = suffix.replace('/user', '')
if (test(attempt)) return attempt
attempt = suffix.replace('/admin/guides', '/admin')
if (test(attempt)) return attempt
attempt = suffix.replace('/admin/guides/user', '/admin/github')
if (test(attempt)) return attempt
attempt = suffix.replace('/admin/guides', '/admin').replace('/user', '/github')
if (test(attempt)) return attempt
return undefined
}