Skip to content

Commit de311a7

Browse files
authored
Convert src/languages/lib to TypeScript (#56572)
1 parent 551da4e commit de311a7

File tree

4 files changed

+97
-32
lines changed

4 files changed

+97
-32
lines changed

src/frame/middleware/context/glossaries.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export default async function glossaries(req: ExtendedRequest, res: Response, ne
6060
)
6161
}
6262
description = await executeWithFallback(
63-
req.context,
63+
req.context!,
6464
() => liquid.parseAndRender(description, req.context),
6565
(enContext: Context) => {
6666
const { term } = glossary

src/languages/lib/correct-translation-content.js renamed to src/languages/lib/correct-translation-content.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,19 @@
77
* It looks for easy "low hanging fruit" that we can correct for.
88
*
99
*/
10-
export function correctTranslatedContentStrings(content, englishContent, context = {}) {
10+
11+
interface CorrectionContext {
12+
code?: string
13+
dottedPath?: string
14+
relativePath?: string
15+
[key: string]: any
16+
}
17+
18+
export function correctTranslatedContentStrings(
19+
content: string,
20+
englishContent: string,
21+
context: CorrectionContext = {},
22+
): string {
1123
// A lot of translations have corruptions around the AUTOTITLE links.
1224
// We've requested that these are corrected back but as a temporary
1325
// solution we'll manually recover now.
@@ -294,7 +306,7 @@ export function correctTranslatedContentStrings(content, englishContent, context
294306
const keyString = '5DE3 E050 9C47 EA3C F04A 42D3 4AEE 18F8 3AFD EB23'
295307
const translatedSentences = [
296308
// ru
297-
'Полный отпечаток ключа — `' + keyString + '`.',
309+
'Полный отпечаток ключа\u00A0\u2014 `' + keyString + '`.',
298310
// ko
299311
`키의 전체 지문은 \`${keyString}\`입니다.`,
300312
// es
@@ -306,7 +318,7 @@ export function correctTranslatedContentStrings(content, englishContent, context
306318
// ja
307319
`キーの完全な指紋は、\`${keyString}\` です。`,
308320
// fr
309-
`L’empreinte digitale complète de la clé est \`${keyString}\`.`,
321+
`L\u2019empreinte digitale complète de la clé est \`${keyString}\`.`,
310322
// de
311323
`Der vollständige Fingerabdruck des Schlüssels ist \`${keyString}\`.`,
312324
]

src/languages/lib/get-english-headings.js renamed to src/languages/lib/get-english-headings.ts

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,40 @@
11
import { fromMarkdown } from 'mdast-util-from-markdown'
22
import { toString } from 'mdast-util-to-string'
33
import { visit } from 'unist-util-visit'
4+
import type { Node } from 'unist'
45

56
import findPage from '@/frame/lib/find-page'
67
import { getDataByLanguage } from '@/data-directory/lib/get-data'
8+
import type { Context } from '@/types'
9+
10+
interface HeadingNode extends Node {
11+
type: 'heading'
12+
depth: number
13+
}
14+
15+
interface PageWithMarkdown {
16+
relativePath: string
17+
markdown: string
18+
}
19+
20+
interface GlossaryTerm {
21+
term: string
22+
slug: string
23+
}
724

825
// for any translated page, first get corresponding English markdown
926
// then get the headings on both the translated and English pageMap
1027
// finally, create a map of translation:English for all headings on the page
11-
export default function getEnglishHeadings(page, context) {
28+
export default function getEnglishHeadings(
29+
page: PageWithMarkdown,
30+
context: Context,
31+
): Record<string, string> | undefined {
1232
// Special handling for glossaries, because their headings are
1333
// generated programmatically.
1434
if (page.relativePath.endsWith('/github-glossary.md')) {
1535
// Return an object of `{ localized-term: english-slug }`
16-
const languageGlossary = getDataByLanguage('glossaries.external', 'en')
17-
return languageGlossary.reduce((prev, curr) => {
36+
const languageGlossary = getDataByLanguage('glossaries.external', 'en') as GlossaryTerm[]
37+
return languageGlossary.reduce((prev: Record<string, string>, curr: GlossaryTerm) => {
1838
prev[curr.term] = curr.slug
1939
return prev
2040
}, {})
@@ -35,20 +55,21 @@ export default function getEnglishHeadings(page, context) {
3555
if (!englishHeadings.length) return
3656

3757
// return a map from translation:English
38-
return Object.assign(
39-
...translatedHeadings.map((k, i) => ({
40-
[k]: englishHeadings[i],
41-
})),
42-
)
58+
const headingMap: Record<string, string> = {}
59+
translatedHeadings.forEach((k: string, i: number) => {
60+
headingMap[k] = englishHeadings[i]
61+
})
62+
return headingMap
4363
}
4464

45-
function getHeadings(markdown) {
65+
function getHeadings(markdown: string): string[] {
4666
const ast = fromMarkdown(markdown)
47-
const headings = []
67+
const headings: string[] = []
4868

49-
visit(ast, (node) => {
69+
visit(ast, (node: Node) => {
5070
if (node.type !== 'heading') return
51-
if (![2, 3, 4].includes(node.depth)) return
71+
const headingNode = node as HeadingNode
72+
if (![2, 3, 4].includes(headingNode.depth)) return
5273
headings.push(toString(node))
5374
})
5475

src/languages/lib/render-with-fallback.js renamed to src/languages/lib/render-with-fallback.ts

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,47 @@
11
import { renderContent } from '@/content-render/index'
22
import Page from '@/frame/lib/page'
33
import { TitleFromAutotitleError } from '@/content-render/unified/rewrite-local-links'
4+
import type { Context } from '@/types'
45

56
export class EmptyTitleError extends Error {}
67

8+
interface LiquidToken {
9+
file?: string
10+
getPosition?: () => [number, number]
11+
}
12+
13+
interface LiquidError extends Error {
14+
token?: LiquidToken
15+
originalError?: Error
16+
}
17+
18+
interface RenderOptions {
19+
throwIfEmpty?: boolean
20+
textOnly?: boolean
21+
cache?: boolean | ((template: string, context: any) => string)
22+
[key: string]: any
23+
}
24+
725
const LIQUID_ERROR_NAMES = new Set(['RenderError', 'ParseError', 'TokenizationError'])
8-
export const isLiquidError = (error) =>
9-
error instanceof Error && error.name && LIQUID_ERROR_NAMES.has(error.name)
26+
export const isLiquidError = (error: unknown): error is LiquidError =>
27+
error instanceof Error && error.name !== undefined && LIQUID_ERROR_NAMES.has(error.name)
1028

11-
const isAutotitleError = (error) => error instanceof TitleFromAutotitleError
12-
const isEmptyTitleError = (error) => error instanceof EmptyTitleError
29+
const isAutotitleError = (error: unknown): error is TitleFromAutotitleError =>
30+
error instanceof TitleFromAutotitleError
1331

14-
const isFallbackableError = (error) =>
32+
const isEmptyTitleError = (error: unknown): error is EmptyTitleError =>
33+
error instanceof EmptyTitleError
34+
35+
const isFallbackableError = (error: unknown): boolean =>
1536
isLiquidError(error) || isAutotitleError(error) || isEmptyTitleError(error)
1637

1738
/**
1839
* Creates an HTML comment with translation fallback error information
1940
* Includes detailed debugging information for translators
2041
*/
21-
export function createTranslationFallbackComment(error, property) {
42+
export function createTranslationFallbackComment(error: Error, property: string): string {
2243
const errorType = error.name || 'UnknownError'
23-
let errorDetails = []
44+
const errorDetails: string[] = []
2445

2546
// Add basic error information
2647
errorDetails.push(`TRANSLATION_FALLBACK`)
@@ -82,14 +103,21 @@ export function createTranslationFallbackComment(error, property) {
82103
// function. This means, we can know, in the middleware (which is a
83104
// higher level than `lib/`) how to use the URL to figure out the
84105
// equivalent English page instance.
85-
export async function renderContentWithFallback(page, property, context, options) {
106+
export async function renderContentWithFallback(
107+
// Using `any` type for page because the actual Page class from @/frame/lib/page
108+
// has more properties than the Page interface defined in @/types, causing type conflicts
109+
page: any,
110+
property: string,
111+
context: Context,
112+
options?: RenderOptions,
113+
): Promise<string> {
86114
if (!(page instanceof Page)) {
87115
throw new Error(`The first argument has to be Page instance (not ${typeof page})`)
88116
}
89117
if (typeof property !== 'string') {
90118
throw new Error(`The second argument has to be a string (not ${typeof property})`)
91119
}
92-
const template = page[property]
120+
const template = (page as any)[property] as string
93121
try {
94122
const output = await renderContent(template, context, options)
95123
if (options && options.throwIfEmpty && !output.trim()) {
@@ -101,7 +129,7 @@ export async function renderContentWithFallback(page, property, context, options
101129
// on English for.
102130
if (isFallbackableError(error) && context.getEnglishPage) {
103131
const enPage = context.getEnglishPage(context)
104-
const englishTemplate = enPage[property]
132+
const englishTemplate = (enPage as any)[property] as string
105133
// If you don't change the context, it'll confuse the liquid plugins
106134
// like `data.js` that uses `environment.scope.currentLanguage`
107135
const enContext = Object.assign({}, context, { currentLanguage: 'en' })
@@ -112,7 +140,7 @@ export async function renderContentWithFallback(page, property, context, options
112140
// Add HTML comment with error details for non-English languages
113141
// Skip for textOnly rendering to avoid breaking plain text output
114142
if (context.currentLanguage !== 'en' && !options?.textOnly) {
115-
const errorComment = createTranslationFallbackComment(error, property)
143+
const errorComment = createTranslationFallbackComment(error as Error, property)
116144
return errorComment + '\n' + fallbackContent
117145
}
118146

@@ -137,19 +165,23 @@ export async function renderContentWithFallback(page, property, context, options
137165
// (enContext) => renderContent(enTrack.title, enContext, renderOpts)
138166
// )
139167
//
140-
export async function executeWithFallback(context, callable, fallback) {
168+
export async function executeWithFallback<T>(
169+
context: Context,
170+
callable: (context: Context) => T | Promise<T>,
171+
fallback: (enContext: Context) => T | Promise<T>,
172+
): Promise<T> {
141173
try {
142-
return await callable(context)
174+
return await Promise.resolve(callable(context))
143175
} catch (error) {
144176
if (isFallbackableError(error) && context.currentLanguage !== 'en') {
145177
const enContext = Object.assign({}, context, { currentLanguage: 'en' })
146-
const fallbackContent = await fallback(enContext)
178+
const fallbackContent = await Promise.resolve(fallback(enContext))
147179

148180
// Add HTML comment with error details for non-English languages
149181
// Only for HTML content (detected by presence of HTML tags)
150182
if (typeof fallbackContent === 'string' && /<[^>]+>/.test(fallbackContent)) {
151-
const errorComment = createTranslationFallbackComment(error, 'content')
152-
return errorComment + '\n' + fallbackContent
183+
const errorComment = createTranslationFallbackComment(error as Error, 'content')
184+
return (errorComment + '\n' + fallbackContent) as T
153185
}
154186

155187
return fallbackContent

0 commit comments

Comments
 (0)