Skip to content

Commit 43206d1

Browse files
authored
Convert landings and products directories JavaScript files to TypeScript (#55560)
1 parent df70f19 commit 43206d1

File tree

10 files changed

+108
-60
lines changed

10 files changed

+108
-60
lines changed

src/events/lib/schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { languageKeys } from '#src/languages/lib/languages.js'
22
import { allVersionKeys } from '#src/versions/lib/all-versions.js'
3-
import { productIds } from '#src/products/lib/all-products.js'
3+
import { productIds } from '#src/products/lib/all-products.ts'
44
import { allTools } from 'src/tools/lib/all-tools.js'
55

66
const versionPattern = '^\\d+(\\.\\d+)?(\\.\\d+)?$' // eslint-disable-line

src/frame/lib/get-toc-items.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { productMap } from '#src/products/lib/all-products.js'
1+
import { productMap } from '#src/products/lib/all-products.ts'
22
const productTOCs = Object.values(productMap)
33
.filter((product) => !product.external)
44
.map((product) => product.toc.replace('content/', ''))

src/frame/lib/page.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import getTocItems from './get-toc-items.js'
99
import Permalink from './permalink.js'
1010
import { renderContent } from '#src/content-render/index.js'
1111
import processLearningTracks from '#src/learning-track/lib/process-learning-tracks.js'
12-
import { productMap } from '#src/products/lib/all-products.js'
12+
import { productMap } from '#src/products/lib/all-products.ts'
1313
import slash from 'slash'
1414
import readFileContents from './read-file-contents.js'
1515
import getLinkData from '#src/learning-track/lib/get-link-data.js'

src/frame/lib/path-utils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import slash from 'slash'
22
import path from 'path'
33
import patterns from './patterns.js'
44
import { latest } from '#src/versions/lib/enterprise-server-releases.js'
5-
import { productIds } from '#src/products/lib/all-products.js'
5+
import { productIds } from '#src/products/lib/all-products.ts'
66
import { allVersions } from '#src/versions/lib/all-versions.js'
77
import nonEnterpriseDefaultVersion from '#src/versions/lib/non-enterprise-default-version.js'
88
const supportedVersions = new Set(Object.keys(allVersions))

src/landings/tests/curated-homepage-links.js renamed to src/landings/tests/curated-homepage-links.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { describe, expect, test, vi } from 'vitest'
2+
import cheerio from 'cheerio'
23

34
import { getDOM } from '#src/tests/helpers/e2etest.js'
45

@@ -11,8 +12,8 @@ describe('curated homepage links', () => {
1112
expect($links.length).toBeGreaterThanOrEqual(6)
1213

1314
// Check that each link is localized and includes a title and intro
14-
$links.each((i, el) => {
15-
const linkUrl = $(el).attr('href')
15+
$links.each((i: number, el: cheerio.Element) => {
16+
const linkUrl = $(el).attr('href') as string
1617

1718
expect(linkUrl.startsWith('/en/')).toBe(true)
1819
expect(
File renamed without changes.

src/products/lib/all-products.js

Lines changed: 0 additions & 49 deletions
This file was deleted.

src/products/lib/all-products.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import fs from 'fs/promises'
2+
import path from 'path'
3+
import frontmatter from '#src/frame/lib/read-frontmatter.js'
4+
import getApplicableVersions from '#src/versions/lib/get-applicable-versions.js'
5+
import removeFPTFromPath from '#src/versions/lib/remove-fpt-from-path.js'
6+
import { ROOT } from '#src/frame/lib/constants.js'
7+
8+
/**
9+
* Represents a product in the documentation
10+
*/
11+
export interface Product {
12+
/** Unique identifier for the product */
13+
id: string
14+
/** Display name of the product (short title or title) */
15+
name: string
16+
/** URL path to the product's landing page */
17+
href: string
18+
/** Directory path to the product's content */
19+
dir: string
20+
/** Path to the product's table of contents file */
21+
toc: string
22+
/** Whether the product is a work in progress */
23+
wip: boolean
24+
/** Whether the product is hidden from the UI */
25+
hidden: boolean
26+
/** Applicable versions for the product */
27+
versions: string[]
28+
/** Rendered name (added later by middleware) */
29+
nameRendered?: string
30+
/** Whether the product is external */
31+
external?: boolean
32+
}
33+
34+
/**
35+
* Map of product IDs to their corresponding product data
36+
*/
37+
export interface ProductMap {
38+
[productId: string]: Product
39+
}
40+
41+
// Both internal and external products are specified in content/index.md
42+
const homepage = path.posix.join(ROOT, 'content/index.md')
43+
export const { data } = frontmatter(await fs.readFile(homepage, 'utf8'))
44+
45+
export const productIds: string[] = data?.children || []
46+
47+
const externalProducts = (data?.externalProducts || {}) as ProductMap
48+
const internalProducts: ProductMap = {}
49+
50+
for (const productId of productIds) {
51+
const relPath = productId
52+
const dir = path.join(ROOT, 'content', relPath)
53+
54+
// Early Access may not exist in the current checkout
55+
try {
56+
await fs.readdir(dir)
57+
} catch {
58+
continue
59+
}
60+
61+
const toc = path.posix.join(dir, 'index.md')
62+
const fileContent = await fs.readFile(toc, 'utf8')
63+
const { data: tocData } = frontmatter(fileContent)
64+
if (tocData) {
65+
const applicableVersions = getApplicableVersions(tocData.versions, toc)
66+
const href = removeFPTFromPath(path.posix.join('/', applicableVersions[0], productId))
67+
68+
// Note that a special middleware called `render-product-map.js` later
69+
// mutates this object by adding a `nameRendered` property to each product.
70+
// It's the outcome of rendering out possible Liquid from the
71+
// `shortTitle` or `title` after all the other contextualizers have run.
72+
internalProducts[productId] = {
73+
id: productId,
74+
name: tocData.shortTitle || tocData.title || productId,
75+
href,
76+
dir,
77+
toc,
78+
wip: tocData.wip || false,
79+
hidden: tocData.hidden || false,
80+
versions: applicableVersions,
81+
}
82+
}
83+
}
84+
85+
export const productMap: ProductMap = Object.assign({}, internalProducts, externalProducts)

src/products/lib/get-product-groups.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,20 +86,30 @@ async function getPage(
8686
}
8787
}
8888

89+
interface ProductGroupData {
90+
name: string
91+
icon?: string
92+
octicon?: string
93+
children: string[]
94+
}
95+
8996
export async function getProductGroups(
9097
pageMap: PageMap,
9198
lang: string,
9299
context: Context,
93100
): Promise<ProductGroup[]> {
101+
// Handle case where data or childGroups might be undefined
102+
const childGroups = data?.childGroups || []
103+
94104
return await Promise.all(
95-
data.childGroups!.map(async (group) => {
105+
childGroups.map(async (group: ProductGroupData) => {
96106
return {
97107
name: group.name,
98108
icon: group.icon || null,
99109
octicon: group.octicon || null,
100110
// Typically the children are product IDs, but we support deeper page paths too
101111
children: (
102-
await Promise.all(group.children.map((id) => getPage(id, lang, pageMap, context)))
112+
await Promise.all(group.children.map((id: string) => getPage(id, lang, pageMap, context)))
103113
).filter(Boolean) as ProductGroupChild[],
104114
}
105115
}),

src/products/tests/products.js renamed to src/products/tests/products.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { describe, expect, test } from 'vitest'
22

33
import { getJsonValidator } from '#src/tests/lib/validate-json-schema.js'
4-
import { productMap } from '#src/products/lib/all-products.js'
4+
import { productMap } from '#src/products/lib/all-products.ts'
55
import { formatAjvErrors } from '#src/tests/helpers/schemas.js'
6+
// @ts-ignore - Products schema doesn't have TypeScript types yet
67
import schema from '#src/tests/helpers/schemas/products-schema.js'
78

89
const validate = getJsonValidator(schema)
@@ -16,9 +17,9 @@ describe('products module', () => {
1617
test('every product is valid', () => {
1718
Object.values(productMap).forEach((product) => {
1819
const isValid = validate(product)
19-
let errors
20+
let errors: string | undefined
2021

21-
if (!isValid) {
22+
if (!isValid && validate.errors) {
2223
errors = formatAjvErrors(validate.errors)
2324
}
2425
expect(isValid, errors).toBe(true)

0 commit comments

Comments
 (0)