1
0
mirror of synced 2025-12-19 09:57:42 -05:00

Replace any types with unknown/specific types (#58658)

This commit is contained in:
Kevin Heis
2025-12-03 07:43:11 -08:00
committed by GitHub
parent 97b71c20d7
commit 03945b8c27
20 changed files with 185 additions and 91 deletions

View File

@@ -109,6 +109,7 @@ export default [
'rest-api-description/',
'docs-internal-data/',
'src/code-scanning/scripts/generate-code-scanning-query-list.ts',
'next-env.d.ts',
],
},

View File

@@ -2,6 +2,7 @@ import { getUIDataMerged } from '@/data-directory/lib/get-data'
import { type LanguageCode } from '@/languages/lib/languages'
import { translate } from '@/languages/lib/translation-utils'
import { extractLanguageFromPath } from '@/app/lib/language-utils'
import { type UIStrings } from '@/frame/components/context/MainContext'
export interface AppRouterContext {
currentLanguage: LanguageCode
@@ -9,7 +10,7 @@ export interface AppRouterContext {
sitename: string
site: {
data: {
ui: any
ui: UIStrings
}
}
}

View File

@@ -100,7 +100,7 @@ export async function getPageInfoFromCache(page: Page, pathname: string) {
cacheInfo = 'initial-load'
} catch (error) {
cacheInfo = 'initial-fail'
if (error instanceof Error && (error as any).code !== 'ENOENT') {
if (error instanceof Error && (error as NodeJS.ErrnoException).code !== 'ENOENT') {
throw error
}
_cache = {}

View File

@@ -3,11 +3,13 @@ import path from 'path'
import { afterAll, beforeAll, describe, expect, test, vi } from 'vitest'
import nock from 'nock'
import type { Response } from 'express'
import { get } from '@/tests/helpers/e2etest'
import { checkCachingHeaders } from '@/tests/helpers/caching-headers'
import { setDefaultFastlySurrogateKey } from '@/frame/middleware/set-fastly-surrogate-key'
import archivedEnterpriseVersionsAssets from '@/archives/middleware/archived-enterprise-versions-assets'
import type { ExtendedRequest } from '@/types'
function getNextStaticAsset(directory: string) {
const root = path.join('.next', 'static', directory)
@@ -34,10 +36,10 @@ function mockRequest(requestPath: string, { headers }: { headers?: Record<string
}
type MockResponse = {
status: number
statusCode: number
json?: (payload: any) => void
send?: (body: any) => void
status: number | undefined
statusCode: number | undefined
json?: (payload: unknown) => void
send?: (body: unknown) => void
sendStatus?: (statusCode: number) => void
end?: () => void
_json?: string
@@ -50,17 +52,17 @@ type MockResponse = {
const mockResponse = () => {
const res: MockResponse = {
status: undefined as any,
statusCode: undefined as any,
status: undefined,
statusCode: undefined,
headers: {},
}
res.json = (payload) => {
res._json = payload
res._json = payload as string
}
res.send = (body) => {
res.status = 200
res.statusCode = 200
res._send = body
res._send = body as string
}
res.end = () => {
// Mock end method
@@ -86,7 +88,7 @@ const mockResponse = () => {
return key in res.headers
}
// Add Express-style status method that supports chaining
;(res as any).status = (code: number) => {
;(res as unknown as { status: (code: number) => MockResponse }).status = (code: number) => {
res.status = code
res.statusCode = code
return res
@@ -222,7 +224,11 @@ describe('archived enterprise static assets', () => {
throw new Error('did not expect this to ever happen')
}
setDefaultFastlySurrogateKey(req, res, () => {})
await archivedEnterpriseVersionsAssets(req as any, res as any, next)
await archivedEnterpriseVersionsAssets(
req as unknown as ExtendedRequest,
res as unknown as Response,
next,
)
expect(res.statusCode).toBe(200)
checkCachingHeaders(res, false, 60)
})
@@ -238,7 +244,11 @@ describe('archived enterprise static assets', () => {
throw new Error('did not expect this to ever happen')
}
setDefaultFastlySurrogateKey(req, res, () => {})
await archivedEnterpriseVersionsAssets(req as any, res as any, next)
await archivedEnterpriseVersionsAssets(
req as unknown as ExtendedRequest,
res as unknown as Response,
next,
)
expect(res.statusCode).toBe(200)
checkCachingHeaders(res, false, 60)
})
@@ -254,7 +264,11 @@ describe('archived enterprise static assets', () => {
throw new Error('did not expect this to ever happen')
}
setDefaultFastlySurrogateKey(req, res, () => {})
await archivedEnterpriseVersionsAssets(req as any, res as any, next)
await archivedEnterpriseVersionsAssets(
req as unknown as ExtendedRequest,
res as unknown as Response,
next,
)
expect(res.statusCode).toBe(200)
checkCachingHeaders(res, false, 60)
})
@@ -271,7 +285,11 @@ describe('archived enterprise static assets', () => {
nexted = true
}
setDefaultFastlySurrogateKey(req, res, next)
await archivedEnterpriseVersionsAssets(req as any, res as any, next)
await archivedEnterpriseVersionsAssets(
req as unknown as ExtendedRequest,
res as unknown as Response,
next,
)
// It didn't exit in that middleware but called next() to move on
// with any other middlewares.
expect(nexted).toBe(true)
@@ -289,7 +307,11 @@ describe('archived enterprise static assets', () => {
nexted = true
}
setDefaultFastlySurrogateKey(req, res, () => {})
await archivedEnterpriseVersionsAssets(req as any, res as any, next)
await archivedEnterpriseVersionsAssets(
req as unknown as ExtendedRequest,
res as unknown as Response,
next,
)
// It tried to go via the proxy, but it wasn't there, but then it
// tried "our disk" and it's eventually there.
expect(nexted).toBe(true)
@@ -335,7 +357,11 @@ describe('archived enterprise static assets', () => {
nexted = true
}
setDefaultFastlySurrogateKey(req, res, () => {})
await archivedEnterpriseVersionsAssets(req as any, res as any, next)
await archivedEnterpriseVersionsAssets(
req as unknown as ExtendedRequest,
res as unknown as Response,
next,
)
expect(res.statusCode).toBe(expectStatus)
if (shouldCallNext) {
expect(nexted).toBe(true)
@@ -374,7 +400,11 @@ describe('archived enterprise static assets', () => {
nexted = true
}
setDefaultFastlySurrogateKey(req, res, () => {})
await archivedEnterpriseVersionsAssets(req as any, res as any, next)
await archivedEnterpriseVersionsAssets(
req as unknown as ExtendedRequest,
res as unknown as Response,
next,
)
expect(nexted).toBe(shouldCallNext)
expect(res.statusCode).toBe(expectStatus)
})

View File

@@ -3,6 +3,7 @@ import path from 'path'
import { readCompressedJsonFileFallback } from '@/frame/lib/read-json-file'
import { getOpenApiVersion } from '@/versions/lib/all-versions'
import findPage from '@/frame/lib/find-page'
import type { Context, Page } from '@/types'
import type {
AuditLogEventT,
CategorizedEvents,
@@ -30,8 +31,8 @@ export function getCategoryNotes(): CategoryNotes {
return auditLogConfig.categoryNotes || {}
}
type TitleResolutionContext = {
pages: Record<string, any>
type TitleResolutionContext = Context & {
pages: Record<string, Page>
redirects: Record<string, string>
}
@@ -61,7 +62,7 @@ async function resolveReferenceLinksToTitles(
currentVersion: 'free-pro-team@latest',
pages: context.pages,
redirects: context.redirects,
}
} as unknown as Context
const title = await page.renderProp('title', renderContext, { textOnly: true })
titles.push(title)
} else {

View File

@@ -14,7 +14,7 @@ import type { MarkdownFrontmatter } from '@/types'
// Type definitions - extending existing type to add missing fields and make most fields optional
type FrontmatterData = Partial<MarkdownFrontmatter> & {
autogenerated?: string
[key: string]: any
[key: string]: unknown
}
type SourceContentItem = {

View File

@@ -1,11 +1,11 @@
import { gitHubDocsMarkdownlint } from '@/content-linter/lib/linting-rules/index'
import { baseConfig } from '@/content-linter/style/base'
import { customConfig } from '@/content-linter/style/github-docs'
import type { Rule } from '@/content-linter/types'
import type { Rule, RuleConfig } from '@/content-linter/types'
// Import markdownlint rules - external library without TypeScript declarations
import markdownlintRules from '../../../../node_modules/markdownlint/lib/rules'
export const customRules: Rule[] = gitHubDocsMarkdownlint.rules
export const allRules: any[] = [...markdownlintRules, ...gitHubDocsMarkdownlint.rules]
export const allConfig: Record<string, any> = { ...baseConfig, ...customConfig }
export const allRules: Rule[] = [...markdownlintRules, ...gitHubDocsMarkdownlint.rules]
export const allConfig = { ...baseConfig, ...customConfig } as unknown as RuleConfig

View File

@@ -7,7 +7,7 @@ import type { RuleParams, RuleErrorCallback, Rule } from '@/content-linter/types
interface Frontmatter {
redirect_from?: string | string[]
children?: string[]
[key: string]: any
[key: string]: unknown
}
const ERROR_MESSAGE =

View File

@@ -7,7 +7,7 @@ import type { RuleParams, RuleErrorCallback, Rule } from '@/content-linter/types
interface Frontmatter {
heroImage?: string
[key: string]: any
[key: string]: unknown
}
// Get the list of valid hero images (without extensions)

View File

@@ -6,7 +6,7 @@ import type { RuleParams, RuleErrorCallback, Rule } from '@/content-linter/types
interface Frontmatter {
introLinks?: Record<string, string>
[key: string]: any
[key: string]: unknown
}
// Get the valid introLinks keys from ui.yml

View File

@@ -5,6 +5,12 @@ import { addError } from 'markdownlint-rule-helpers'
import { getFrontmatter } from '../helpers/utils'
import type { RuleParams, RuleErrorCallback } from '@/content-linter/types'
interface Frontmatter {
recommended?: string[]
layout?: string
[key: string]: unknown
}
function isValidArticlePath(articlePath: string, currentFilePath: string): boolean {
const ROOT = process.env.ROOT || '.'
@@ -53,7 +59,7 @@ export const frontmatterLandingRecommended = {
tags: ['frontmatter', 'landing', 'recommended'],
function: (params: RuleParams, onError: RuleErrorCallback) => {
// Using any for frontmatter as it's a dynamic YAML object with varying properties
const fm: any = getFrontmatter(params.lines)
const fm = getFrontmatter(params.lines) as Frontmatter | null
if (!fm || !fm.recommended) return
const recommendedLine: string | undefined = params.lines.find((line) =>

View File

@@ -37,7 +37,9 @@ export const frontmatterSchema: Rule = {
// Check that the frontmatter matches the schema
const { errors } = readFrontmatter(params.lines.join('\n'), { schema: frontmatter.schema })
const formattedErrors = formatAjvErrors(errors as any)
const formattedErrors = formatAjvErrors(
errors as unknown as Parameters<typeof formatAjvErrors>[0],
)
for (const error of formattedErrors) {
// If the missing property is at the top level, we don't have a line
// to point to. In that case, the error will be added to line 1.

View File

@@ -4,7 +4,7 @@ import type { RuleParams, RuleErrorCallback, Rule } from '@/content-linter/types
interface Frontmatter {
versions?: Record<string, string | string[]>
[key: string]: any
[key: string]: unknown
}
export const frontmatterVersionsWhitespace: Rule = {

View File

@@ -9,7 +9,7 @@ interface Frontmatter {
product_video_transcript?: string
title?: string
layout?: string
[key: string]: any
[key: string]: unknown
}
export const frontmatterVideoTranscripts: Rule = {

View File

@@ -672,6 +672,7 @@ function getMarkdownLintConfig(
const ymlSearchReplaceRules = []
const frontmatterSearchReplaceRules = []
if (!ruleConfig.rules) continue
for (const searchRule of ruleConfig.rules) {
const searchRuleSeverity = getSeverity(searchRule, isPrecommit)
if (filterErrorsOnly && searchRuleSeverity !== 'error') continue

View File

@@ -16,7 +16,7 @@ export interface RuleParams {
frontMatterLines: string[] // array of frontmatter lines
tokens?: MarkdownToken[] // markdown tokens (when using markdownit parser)
config?: {
[key: string]: any // rule-specific configuration
[key: string]: unknown // rule-specific configuration
}
}
@@ -26,7 +26,7 @@ export interface RuleErrorCallback {
detail?: string,
context?: string,
range?: [number, number],
fixInfo?: any,
fixInfo?: unknown,
): void
}
@@ -44,6 +44,8 @@ export type Rule = {
type RuleDetail = Rule & {
name: string
'partial-markdown-files': boolean
'yml-files'?: boolean
applyToFrontmatter?: boolean
message: string
severity: string
searchPattern: string
@@ -54,6 +56,7 @@ type RuleDetail = Rule & {
export type Config = {
severity: string
'partial-markdown-files': boolean
'yml-files'?: boolean
allowed_languages?: string[]
style?: string
rules?: RuleDetail[]

View File

@@ -29,13 +29,14 @@ export default async function categoriesForSupport(req: ExtendedRequest, res: Re
// We can't get the rendered titles from middleware/render-tree-titles
// here because that middleware only runs on the current version, and this
// middleware processes all versions.
if (!req.context) return
const name = categoryPage.page.title.includes('{')
? await categoryPage.page.renderProp('title', req.context, renderOpts)
: categoryPage.page.title
allCategories.push({
name,
published_articles: await findArticlesPerCategory(categoryPage, [], req.context!),
published_articles: await findArticlesPerCategory(categoryPage, [], req.context),
})
}),
)

View File

@@ -106,7 +106,7 @@ export default async function renderPage(req: ExtendedRequest, res: Response) {
req.context.currentVersion === 'free-pro-team@latest' ||
!allVersions[req.context.currentVersion!]
) {
page.fullTitle += ` - ${context.site!.data.ui.header.github_docs}`
page.fullTitle += ` - ${get(context.site!.data.ui, 'header.github_docs')}`
} else {
const { versionTitle } = allVersions[req.context.currentVersion!]
page.fullTitle += ' - '

View File

@@ -1,6 +1,7 @@
import { describe, expect, test } from 'vitest'
import getRedirect from '../../lib/get-redirect'
import type { Context } from '@/types'
import {
latest,
latestStable,
@@ -10,7 +11,8 @@ import {
// Test helper type for mocking contexts
type TestContext = {
pages: Record<string, any>
[key: string]: unknown
pages: Record<string, unknown>
redirects: Record<string, string>
}
@@ -31,7 +33,7 @@ describe('getRedirect basics', () => {
'/enterprise/3.0/foo/bar': '/something/else',
},
}
expect(getRedirect(uri, ctx)).toBe('/en/something/else')
expect(getRedirect(uri, ctx as unknown as Context)).toBe('/en/something/else')
})
test('should return undefined if nothing could be found', () => {
@@ -39,7 +41,7 @@ describe('getRedirect basics', () => {
pages: {},
redirects: {},
}
expect(getRedirect('/foo/pizza', ctx)).toBeUndefined()
expect(getRedirect('/foo/pizza', ctx as unknown as Context)).toBeUndefined()
})
test('should just inject language on version "home pages"', () => {
@@ -47,16 +49,20 @@ describe('getRedirect basics', () => {
pages: {},
redirects: {},
}
expect(getRedirect('/enterprise-cloud@latest', ctx)).toBe('/en/enterprise-cloud@latest')
expect(getRedirect('/enterprise-cloud@latest', ctx as unknown as Context)).toBe(
'/en/enterprise-cloud@latest',
)
expect(getRedirect(`/enterprise-server@${oldestSupported}`, ctx)).toBe(
expect(getRedirect(`/enterprise-server@${oldestSupported}`, ctx as unknown as Context)).toBe(
`/en/enterprise-server@${oldestSupported}`,
)
expect(getRedirect('/enterprise-server@latest', ctx)).toBe(
expect(getRedirect('/enterprise-server@latest', ctx as unknown as Context)).toBe(
`/en/enterprise-server@${latestStable}`,
)
expect(getRedirect('/enterprise-server', ctx as unknown as Context)).toBe(
`/en/enterprise-server@${latestStable}`,
)
expect(getRedirect('/enterprise-server', ctx)).toBe(`/en/enterprise-server@${latestStable}`)
})
test('should always "remove" the free-pro-team prefix', () => {
@@ -66,12 +72,14 @@ describe('getRedirect basics', () => {
'/foo': '/bar',
},
}
expect(getRedirect('/free-pro-team@latest', ctx)).toBe('/en')
expect(getRedirect('/free-pro-team@latest', ctx as unknown as Context)).toBe('/en')
// Language is fine, but the version needs to be "removed"
expect(getRedirect('/en/free-pro-team@latest', ctx)).toBe('/en')
expect(getRedirect('/free-pro-team@latest/pizza', ctx)).toBe('/en/pizza')
expect(getRedirect('/free-pro-team@latest/foo', ctx)).toBe('/en/bar')
expect(getRedirect('/free-pro-team@latest/github', ctx)).toBe('/en/github')
expect(getRedirect('/en/free-pro-team@latest', ctx as unknown as Context)).toBe('/en')
expect(getRedirect('/free-pro-team@latest/pizza', ctx as unknown as Context)).toBe('/en/pizza')
expect(getRedirect('/free-pro-team@latest/foo', ctx as unknown as Context)).toBe('/en/bar')
expect(getRedirect('/free-pro-team@latest/github', ctx as unknown as Context)).toBe(
'/en/github',
)
})
test('should handle some odd exceptions', () => {
@@ -79,14 +87,16 @@ describe('getRedirect basics', () => {
pages: {},
redirects: {},
}
expect(getRedirect('/desktop/guides/foo/bar', ctx)).toBe('/en/desktop/foo/bar')
expect(getRedirect('/admin/guides/foo/bar', ctx)).toBe(
expect(getRedirect('/desktop/guides/foo/bar', ctx as unknown as Context)).toBe(
'/en/desktop/foo/bar',
)
expect(getRedirect('/admin/guides/foo/bar', ctx as unknown as Context)).toBe(
`/en/enterprise-server@${latest}/admin/foo/bar`,
)
expect(getRedirect('/admin/something/else', ctx)).toBe(
expect(getRedirect('/admin/something/else', ctx as unknown as Context)).toBe(
`/en/enterprise-server@${latest}/admin/something/else`,
)
expect(getRedirect('/insights/stuff', ctx)).toBe(
expect(getRedirect('/insights/stuff', ctx as unknown as Context)).toBe(
`/en/enterprise-server@${latest}/insights/stuff`,
)
})
@@ -101,12 +111,15 @@ describe('getRedirect basics', () => {
}
// Replacing `/user` with `` worked because there exits a page of such name.
expect(
getRedirect(`/enterprise-server@${previousEnterpriserServerVersion}/user/foo/bar`, ctx),
getRedirect(
`/enterprise-server@${previousEnterpriserServerVersion}/user/foo/bar`,
ctx as unknown as Context,
),
).toBe(`/en/enterprise-server@${previousEnterpriserServerVersion}/foo/bar`)
expect(
getRedirect(
`/enterprise-server@${previousEnterpriserServerVersion}/admin/guides/user-management`,
ctx,
ctx as unknown as Context,
),
).toBe(`/en/enterprise-server@${previousEnterpriserServerVersion}/admin/github-management`)
})
@@ -118,20 +131,25 @@ describe('getRedirect basics', () => {
[`/enterprise-server@${previousEnterpriserServerVersion}/foo`]: `/enterprise-server@${previousEnterpriserServerVersion}/bar`,
},
}
expect(getRedirect('/enterprise', ctx)).toBe(`/en/enterprise-server@${latest}`)
expect(getRedirect(`/enterprise/${previousEnterpriserServerVersion}`, ctx)).toBe(
`/en/enterprise-server@${previousEnterpriserServerVersion}`,
)
expect(getRedirect(`/enterprise/${previousEnterpriserServerVersion}/something`, ctx)).toBe(
`/en/enterprise-server@${previousEnterpriserServerVersion}/something`,
expect(getRedirect('/enterprise', ctx as unknown as Context)).toBe(
`/en/enterprise-server@${latest}`,
)
expect(
getRedirect(`/enterprise/${previousEnterpriserServerVersion}`, ctx as unknown as Context),
).toBe(`/en/enterprise-server@${previousEnterpriserServerVersion}`)
expect(
getRedirect(
`/enterprise/${previousEnterpriserServerVersion}/something`,
ctx as unknown as Context,
),
).toBe(`/en/enterprise-server@${previousEnterpriserServerVersion}/something`)
// but also respect redirects if there are some
expect(getRedirect(`/enterprise/${previousEnterpriserServerVersion}/foo`, ctx)).toBe(
`/en/enterprise-server@${previousEnterpriserServerVersion}/bar`,
)
expect(
getRedirect(`/enterprise/${previousEnterpriserServerVersion}/foo`, ctx as unknown as Context),
).toBe(`/en/enterprise-server@${previousEnterpriserServerVersion}/bar`)
// Unique snowflake pattern
expect(getRedirect('/enterprise/github/admin/foo', ctx)).toBe(
expect(getRedirect('/enterprise/github/admin/foo', ctx as unknown as Context)).toBe(
`/en/enterprise-server@${latest}/github/admin/foo`,
)
})
@@ -143,8 +161,15 @@ describe('getRedirect basics', () => {
}
// Nothing's needed here because it's not /admin/guides and
// it already has the enterprise-server prefix.
expect(getRedirect(`/en/enterprise-server@${latest}/admin/something/else`, ctx)).toBeUndefined()
expect(getRedirect(`/en/enterprise-cloud@latest/user/foo`, ctx)).toBeUndefined()
expect(
getRedirect(
`/en/enterprise-server@${latest}/admin/something/else`,
ctx as unknown as Context,
),
).toBeUndefined()
expect(
getRedirect(`/en/enterprise-cloud@latest/user/foo`, ctx as unknown as Context),
).toBeUndefined()
})
test('should redirect both the prefix and the path needs to change', () => {
@@ -157,7 +182,7 @@ describe('getRedirect basics', () => {
}
// Nothing's needed here because it's not /admin/guides and
// it already has the enterprise-server prefix.
expect(getRedirect('/enterprise-server/foo', ctx)).toBe(
expect(getRedirect('/enterprise-server/foo', ctx as unknown as Context)).toBe(
`/en/enterprise-server@${latestStable}/bar`,
)
})
@@ -169,8 +194,12 @@ describe('getRedirect basics', () => {
pages: {},
redirects: {},
}
expect(getRedirect('/enterprise/3.0', ctx)).toBe('/en/enterprise-server@3.0')
expect(getRedirect('/enterprise/3.0/foo', ctx)).toBe('/en/enterprise-server@3.0/foo')
expect(getRedirect('/enterprise/3.0', ctx as unknown as Context)).toBe(
'/en/enterprise-server@3.0',
)
expect(getRedirect('/enterprise/3.0/foo', ctx as unknown as Context)).toBe(
'/en/enterprise-server@3.0/foo',
)
})
})
@@ -180,16 +209,24 @@ describe('github-ae@latest', () => {
pages: {},
redirects: {},
}
expect(getRedirect('/github-ae@latest', ctx)).toBe('/en/enterprise-cloud@latest')
expect(getRedirect('/en/github-ae@latest', ctx)).toBe('/en/enterprise-cloud@latest')
expect(getRedirect('/github-ae@latest', ctx as unknown as Context)).toBe(
'/en/enterprise-cloud@latest',
)
expect(getRedirect('/en/github-ae@latest', ctx as unknown as Context)).toBe(
'/en/enterprise-cloud@latest',
)
})
test('should redirect to home page for admin/release-notes', () => {
const ctx: TestContext = {
pages: {},
redirects: {},
}
expect(getRedirect('/github-ae@latest/admin/release-notes', ctx)).toBe('/en')
expect(getRedirect('/en/github-ae@latest/admin/release-notes', ctx)).toBe('/en')
expect(getRedirect('/github-ae@latest/admin/release-notes', ctx as unknown as Context)).toBe(
'/en',
)
expect(getRedirect('/en/github-ae@latest/admin/release-notes', ctx as unknown as Context)).toBe(
'/en',
)
})
test('a page that does exits, without correction, in enterprise-cloud', () => {
const ctx: TestContext = {
@@ -198,8 +235,12 @@ describe('github-ae@latest', () => {
},
redirects: {},
}
expect(getRedirect('/github-ae@latest/foo', ctx)).toBe('/en/enterprise-cloud@latest/foo')
expect(getRedirect('/en/github-ae@latest/foo', ctx)).toBe('/en/enterprise-cloud@latest/foo')
expect(getRedirect('/github-ae@latest/foo', ctx as unknown as Context)).toBe(
'/en/enterprise-cloud@latest/foo',
)
expect(getRedirect('/en/github-ae@latest/foo', ctx as unknown as Context)).toBe(
'/en/enterprise-cloud@latest/foo',
)
})
test("a page that doesn't exist in enterprise-cloud but in FPT", () => {
const ctx: TestContext = {
@@ -208,8 +249,8 @@ describe('github-ae@latest', () => {
},
redirects: {},
}
expect(getRedirect('/github-ae@latest/foo', ctx)).toBe('/en/foo')
expect(getRedirect('/en/github-ae@latest/foo', ctx)).toBe('/en/foo')
expect(getRedirect('/github-ae@latest/foo', ctx as unknown as Context)).toBe('/en/foo')
expect(getRedirect('/en/github-ae@latest/foo', ctx as unknown as Context)).toBe('/en/foo')
})
test("a page that doesn't exist in enterprise-cloud or in FPT", () => {
const ctx: TestContext = {
@@ -218,8 +259,8 @@ describe('github-ae@latest', () => {
},
redirects: {},
}
expect(getRedirect('/github-ae@latest/bar', ctx)).toBe('/en')
expect(getRedirect('/en/github-ae@latest/bar', ctx)).toBe('/en')
expect(getRedirect('/github-ae@latest/bar', ctx as unknown as Context)).toBe('/en')
expect(getRedirect('/en/github-ae@latest/bar', ctx as unknown as Context)).toBe('/en')
})
test('a URL with legacy redirects, that redirects to enterprise-cloud', () => {
const ctx: TestContext = {
@@ -231,8 +272,12 @@ describe('github-ae@latest', () => {
'/food': '/foo',
},
}
expect(getRedirect('/github-ae@latest/food', ctx)).toBe('/en/enterprise-cloud@latest/foo')
expect(getRedirect('/en/github-ae@latest/food', ctx)).toBe('/en/enterprise-cloud@latest/foo')
expect(getRedirect('/github-ae@latest/food', ctx as unknown as Context)).toBe(
'/en/enterprise-cloud@latest/foo',
)
expect(getRedirect('/en/github-ae@latest/food', ctx as unknown as Context)).toBe(
'/en/enterprise-cloud@latest/foo',
)
})
test("a URL with legacy redirects, that can't redirect to enterprise-cloud", () => {
const ctx: TestContext = {
@@ -244,15 +289,17 @@ describe('github-ae@latest', () => {
'/food': '/foo',
},
}
expect(getRedirect('/github-ae@latest/food', ctx)).toBe('/en/foo')
expect(getRedirect('/en/github-ae@latest/food', ctx)).toBe('/en/foo')
expect(getRedirect('/github-ae@latest/food', ctx as unknown as Context)).toBe('/en/foo')
expect(getRedirect('/en/github-ae@latest/food', ctx as unknown as Context)).toBe('/en/foo')
})
test('should 404 if nothing matches at all', () => {
const ctx = {
pages: {},
redirects: {},
}
expect(getRedirect('/github-ae@latest/never/heard/of', ctx)).toBe('/en')
expect(getRedirect('/en/github-ae@latest/never/heard/of', ctx)).toBe('/en')
expect(getRedirect('/github-ae@latest/never/heard/of', ctx as unknown as Context)).toBe('/en')
expect(getRedirect('/en/github-ae@latest/never/heard/of', ctx as unknown as Context)).toBe(
'/en',
)
})
})

View File

@@ -5,6 +5,7 @@ import type enterpriseServerReleases from '@/versions/lib/enterprise-server-rele
import type { ValidOcticon } from '@/landings/types'
import type { Language, Languages } from '@/languages/lib/languages-server'
import type { MiniTocItem } from '@/frame/lib/get-mini-toc-items'
import type { UIStrings } from '@/frame/components/context/MainContext'
// Shared type for resolved article information used across landing pages and carousels
export interface ResolvedArticle {
@@ -121,7 +122,7 @@ type Redirects = {
export type Context = {
// Allows dynamic properties like features & version shortnames as keys
[key: string]: any
[key: string]: unknown
currentCategory?: string
error?: Error
siteTree?: SiteTree
@@ -134,7 +135,7 @@ export type Context = {
allVersions?: AllVersions
currentPathWithoutLanguage?: string
currentArticle?: string
query?: Record<string, any>
query?: Record<string, unknown>
relativePath?: string
page?: Page
enPage?: Page
@@ -143,13 +144,13 @@ export type Context = {
process?: { env: Record<string, string> }
site?: {
data: {
ui: any
ui: UIStrings
}
}
currentVersionObj?: Version
currentProduct?: string
getEnglishPage?: (ctx: Context) => Page
getDottedData?: (dottedPath: string) => any
getDottedData?: (dottedPath: string) => unknown
initialRestVersioningReleaseDate?: string
initialRestVersioningReleaseDateLong?: string
nonEnterpriseDefaultVersion?: string
@@ -360,8 +361,8 @@ export type Page = {
rawPermissions?: string
languageCode: string
documentType: string
renderProp: (prop: string, context: any, opts?: any) => Promise<string>
renderTitle: (context: Context, opts?: any) => Promise<string>
renderProp: (prop: string, context: Context, opts?: Record<string, unknown>) => Promise<string>
renderTitle: (context: Context, opts?: Record<string, unknown>) => Promise<string>
markdown: string
versions: FrontmatterVersions
applicableVersions: string[]