From 03945b8c278a4c7ce27e94271896fbe25e15ee31 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Wed, 3 Dec 2025 07:43:11 -0800 Subject: [PATCH] Replace any types with unknown/specific types (#58658) --- eslint.config.ts | 1 + src/app/lib/app-router-context.ts | 3 +- .../middleware/article-pageinfo.ts | 2 +- src/assets/tests/static-assets.ts | 62 ++++++-- src/audit-logs/lib/index.ts | 7 +- .../lib/update-markdown.ts | 2 +- src/content-linter/lib/helpers/get-rules.ts | 6 +- .../linting-rules/early-access-references.ts | 2 +- .../linting-rules/frontmatter-hero-image.ts | 2 +- .../linting-rules/frontmatter-intro-links.ts | 2 +- .../frontmatter-landing-recommended.ts | 8 +- .../lib/linting-rules/frontmatter-schema.ts | 4 +- .../frontmatter-versions-whitespace.ts | 2 +- .../frontmatter-video-transcripts.ts | 2 +- src/content-linter/scripts/lint-content.ts | 1 + src/content-linter/types.ts | 7 +- .../middleware/categories-for-support.ts | 3 +- src/frame/middleware/render-page.ts | 2 +- src/redirects/tests/unit/get-redirect.ts | 145 ++++++++++++------ src/types/types.ts | 13 +- 20 files changed, 185 insertions(+), 91 deletions(-) diff --git a/eslint.config.ts b/eslint.config.ts index ad1d60c95db7..c0937d6df79c 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -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', ], }, diff --git a/src/app/lib/app-router-context.ts b/src/app/lib/app-router-context.ts index 3e0e8e75ecc2..455705576dec 100644 --- a/src/app/lib/app-router-context.ts +++ b/src/app/lib/app-router-context.ts @@ -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 } } } diff --git a/src/article-api/middleware/article-pageinfo.ts b/src/article-api/middleware/article-pageinfo.ts index b9d45d80bd59..87f648821792 100644 --- a/src/article-api/middleware/article-pageinfo.ts +++ b/src/article-api/middleware/article-pageinfo.ts @@ -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 = {} diff --git a/src/assets/tests/static-assets.ts b/src/assets/tests/static-assets.ts index 4a1c9ed217e8..b84be4d102f8 100644 --- a/src/assets/tests/static-assets.ts +++ b/src/assets/tests/static-assets.ts @@ -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 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) }) diff --git a/src/audit-logs/lib/index.ts b/src/audit-logs/lib/index.ts index 2ffd8aece86f..b81158f63979 100644 --- a/src/audit-logs/lib/index.ts +++ b/src/audit-logs/lib/index.ts @@ -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 +type TitleResolutionContext = Context & { + pages: Record redirects: Record } @@ -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 { diff --git a/src/automated-pipelines/lib/update-markdown.ts b/src/automated-pipelines/lib/update-markdown.ts index 7ea72580ef7d..6ca36f482b71 100644 --- a/src/automated-pipelines/lib/update-markdown.ts +++ b/src/automated-pipelines/lib/update-markdown.ts @@ -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 & { autogenerated?: string - [key: string]: any + [key: string]: unknown } type SourceContentItem = { diff --git a/src/content-linter/lib/helpers/get-rules.ts b/src/content-linter/lib/helpers/get-rules.ts index c06c2c711495..a27587067fc0 100644 --- a/src/content-linter/lib/helpers/get-rules.ts +++ b/src/content-linter/lib/helpers/get-rules.ts @@ -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 = { ...baseConfig, ...customConfig } +export const allRules: Rule[] = [...markdownlintRules, ...gitHubDocsMarkdownlint.rules] +export const allConfig = { ...baseConfig, ...customConfig } as unknown as RuleConfig diff --git a/src/content-linter/lib/linting-rules/early-access-references.ts b/src/content-linter/lib/linting-rules/early-access-references.ts index 76838d95a517..c80e2642a9d8 100644 --- a/src/content-linter/lib/linting-rules/early-access-references.ts +++ b/src/content-linter/lib/linting-rules/early-access-references.ts @@ -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 = diff --git a/src/content-linter/lib/linting-rules/frontmatter-hero-image.ts b/src/content-linter/lib/linting-rules/frontmatter-hero-image.ts index 6aa96af64d64..28f3cf26cfce 100644 --- a/src/content-linter/lib/linting-rules/frontmatter-hero-image.ts +++ b/src/content-linter/lib/linting-rules/frontmatter-hero-image.ts @@ -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) diff --git a/src/content-linter/lib/linting-rules/frontmatter-intro-links.ts b/src/content-linter/lib/linting-rules/frontmatter-intro-links.ts index fbf6cc1e0e61..3e368582818f 100644 --- a/src/content-linter/lib/linting-rules/frontmatter-intro-links.ts +++ b/src/content-linter/lib/linting-rules/frontmatter-intro-links.ts @@ -6,7 +6,7 @@ import type { RuleParams, RuleErrorCallback, Rule } from '@/content-linter/types interface Frontmatter { introLinks?: Record - [key: string]: any + [key: string]: unknown } // Get the valid introLinks keys from ui.yml diff --git a/src/content-linter/lib/linting-rules/frontmatter-landing-recommended.ts b/src/content-linter/lib/linting-rules/frontmatter-landing-recommended.ts index e146b2848b0a..ac6b9894b3cd 100644 --- a/src/content-linter/lib/linting-rules/frontmatter-landing-recommended.ts +++ b/src/content-linter/lib/linting-rules/frontmatter-landing-recommended.ts @@ -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) => diff --git a/src/content-linter/lib/linting-rules/frontmatter-schema.ts b/src/content-linter/lib/linting-rules/frontmatter-schema.ts index 261d9d990058..b91f7590ce85 100644 --- a/src/content-linter/lib/linting-rules/frontmatter-schema.ts +++ b/src/content-linter/lib/linting-rules/frontmatter-schema.ts @@ -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[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. diff --git a/src/content-linter/lib/linting-rules/frontmatter-versions-whitespace.ts b/src/content-linter/lib/linting-rules/frontmatter-versions-whitespace.ts index b38750a2b341..30ed2dc89921 100644 --- a/src/content-linter/lib/linting-rules/frontmatter-versions-whitespace.ts +++ b/src/content-linter/lib/linting-rules/frontmatter-versions-whitespace.ts @@ -4,7 +4,7 @@ import type { RuleParams, RuleErrorCallback, Rule } from '@/content-linter/types interface Frontmatter { versions?: Record - [key: string]: any + [key: string]: unknown } export const frontmatterVersionsWhitespace: Rule = { diff --git a/src/content-linter/lib/linting-rules/frontmatter-video-transcripts.ts b/src/content-linter/lib/linting-rules/frontmatter-video-transcripts.ts index c4062acc5185..2d5d7fe0fc86 100644 --- a/src/content-linter/lib/linting-rules/frontmatter-video-transcripts.ts +++ b/src/content-linter/lib/linting-rules/frontmatter-video-transcripts.ts @@ -9,7 +9,7 @@ interface Frontmatter { product_video_transcript?: string title?: string layout?: string - [key: string]: any + [key: string]: unknown } export const frontmatterVideoTranscripts: Rule = { diff --git a/src/content-linter/scripts/lint-content.ts b/src/content-linter/scripts/lint-content.ts index a5ab8ef7f7b8..585a0cc3a1a5 100755 --- a/src/content-linter/scripts/lint-content.ts +++ b/src/content-linter/scripts/lint-content.ts @@ -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 diff --git a/src/content-linter/types.ts b/src/content-linter/types.ts index 53048ac01811..43f1b3cdf9b2 100644 --- a/src/content-linter/types.ts +++ b/src/content-linter/types.ts @@ -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[] diff --git a/src/frame/middleware/categories-for-support.ts b/src/frame/middleware/categories-for-support.ts index 72e12e190a48..ad8bae698a57 100644 --- a/src/frame/middleware/categories-for-support.ts +++ b/src/frame/middleware/categories-for-support.ts @@ -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), }) }), ) diff --git a/src/frame/middleware/render-page.ts b/src/frame/middleware/render-page.ts index eeb2eb0bf929..2dbc8b0415f1 100644 --- a/src/frame/middleware/render-page.ts +++ b/src/frame/middleware/render-page.ts @@ -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 += ' - ' diff --git a/src/redirects/tests/unit/get-redirect.ts b/src/redirects/tests/unit/get-redirect.ts index 918abd35e9ca..f518eac0f64c 100644 --- a/src/redirects/tests/unit/get-redirect.ts +++ b/src/redirects/tests/unit/get-redirect.ts @@ -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 + [key: string]: unknown + pages: Record redirects: Record } @@ -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', + ) }) }) diff --git a/src/types/types.ts b/src/types/types.ts index b39896326926..dddf8cc4bc66 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -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 + query?: Record relativePath?: string page?: Page enPage?: Page @@ -143,13 +144,13 @@ export type Context = { process?: { env: Record } 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 - renderTitle: (context: Context, opts?: any) => Promise + renderProp: (prop: string, context: Context, opts?: Record) => Promise + renderTitle: (context: Context, opts?: Record) => Promise markdown: string versions: FrontmatterVersions applicableVersions: string[]