forked from github/docs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathget-redirect.js
212 lines (188 loc) · 8.07 KB
/
get-redirect.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
import { languageKeys } from './languages.js'
import nonEnterpriseDefaultVersion from './non-enterprise-default-version.js'
import { allVersions } from './all-versions.js'
import {
latest,
supported,
deprecatedWithFunctionalRedirects,
} from './enterprise-server-releases.js'
const languagePrefixRegex = new RegExp(`^/(${languageKeys.join('|')})/`)
const nonEnterpriseDefaultVersionPrefix = `/${nonEnterpriseDefaultVersion}`
const supportedAndRecentlyDeprecated = [...supported, ...deprecatedWithFunctionalRedirects]
export function splitPathByLanguage(uri, userLanguage) {
let language = userLanguage || 'en'
let withoutLanguage = uri
if (languagePrefixRegex.test(uri)) {
language = uri.match(languagePrefixRegex)[1]
withoutLanguage = uri.replace(languagePrefixRegex, '/')
}
return [language, withoutLanguage]
}
// Return the new URI if there is one, otherwise return undefined.
export default function getRedirect(uri, context) {
const { redirects, userLanguage } = context
const [language, withoutLanguage] = splitPathByLanguage(uri, userLanguage)
let destination
// `redirects` is sourced from more than one thing. The primary use
// case is gathering up the `redirect_from` frontmatter key.
// But we also has `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) {
return `/${language}` + redirects[withoutLanguage]
}
let basicCorrection
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@${latest}`)
// 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@${latest}`)
// 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 '/[email protected]'
const majorVersion = withoutLanguage.split('/')[1].split('@')[0]
const split = withoutLanguage.split('/')
const version = split[1].split('@')[1]
let prefix
let suffix
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
}
}
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}`
}
}
// 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, suffix, { pages, redirects }) {
const test = (suffix) => {
// 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 (suffix.includes('/rest')) {
return false
}
const candidateAsRedirect = prefix + suffix
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
}