Skip to content

Commit ff217a6

Browse files
heiskrCopilot
andauthored
Tighten code comments in src/landings lib, middleware, pages, and tests (#63455)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7c52b57f-2c29-4aa1-8233-99d0d6e13571 Copilot-Session: 24b9b4c0-b2dc-4705-aa02-7c9516bbb806
1 parent 68261af commit ff217a6

19 files changed

Lines changed: 67 additions & 130 deletions

‎src/landings/context/LandingContext.tsx‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,13 @@ export type LandingContextT = {
1919
renderedPage: string
2020
currentLayout: string
2121
heroImage?: string
22-
// For landing pages with carousels
2322
carousels?: Record<
2423
string,
2524
Array<{ title: string; intro: string; href: string; category: string[] }>
2625
>
2726
introLinks?: Record<string, string> | null
28-
// For journey landing pages
2927
journeyTracks?: JourneyTrack[]
3028
journeyArticlesHeading?: string | null
31-
// For article grid category filtering
3229
includedCategories?: string[]
3330
}
3431

@@ -77,8 +74,7 @@ export const getLandingContextFromRequest = async (
7774
? (page.carousels as LandingContextT['carousels'])
7875
: {}
7976

80-
// Note: Journey tracks are resolved in middleware and added to the request
81-
// context to avoid the error using server side apis client side
77+
// Middleware resolves journey tracks because server-side APIs cannot run client-side.
8278
const journeyTracks: JourneyTrack[] = Array.isArray(context.journeyTracks)
8379
? context.journeyTracks
8480
: Array.isArray(page.resolvedJourneyTracks)

‎src/landings/lib/article-search.ts‎

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ import { fuzzyMatchScore, stripStopWords } from '@/landings/lib/fuzzy-match'
33

44
const STOP_WORD_THRESHOLD = 0.8
55

6-
// Recursively flatten nested TOC items into leaf articles.
7-
// Excludes index pages (pages with childTocItems).
6+
// Parents with childTocItems are index pages, not article cards.
87
const flattenArticlesRecursive = (articles: (TocItem | ChildTocItem)[]): ArticleCardItems => {
98
const flattened: ArticleCardItems = []
109

@@ -19,7 +18,6 @@ const flattenArticlesRecursive = (articles: (TocItem | ChildTocItem)[]): Article
1918
return flattened
2019
}
2120

22-
// Flatten, deduplicate by fullPath, and sort alphabetically by title.
2321
export const flattenArticles = (articles: (TocItem | ChildTocItem)[]): ArticleCardItems => {
2422
const flattened = flattenArticlesRecursive(articles)
2523
const seen = new Set<string>()
@@ -31,8 +29,8 @@ export const flattenArticles = (articles: (TocItem | ChildTocItem)[]): ArticleCa
3129
return deduped.sort((a, b) => a.title.localeCompare(b.title))
3230
}
3331

34-
// Find words appearing in a high percentage of article titles/intros.
35-
// These add little signal to search since they match nearly everything.
32+
// Words that appear in most titles or intros add little signal because they
33+
// match nearly every article.
3634
export const deriveStopWords = (
3735
articles: ArticleCardItems,
3836
threshold = STOP_WORD_THRESHOLD,
@@ -49,9 +47,8 @@ export const deriveStopWords = (
4947
return [...wordCounts.entries()].filter(([, count]) => count >= minCount).map(([word]) => word)
5048
}
5149

52-
// Score and rank articles against a search query, returning only matches.
53-
// Searches title, intro, and category fields. Returns all articles (scored 0.5)
54-
// when the query consists entirely of stop words.
50+
// Search matches title, intro, and category fields; queries made only of stop
51+
// words return every article with a neutral 0.5 score.
5552
export const searchArticles = (
5653
articles: ArticleCardItems,
5754
query: string,

‎src/landings/lib/count-articles.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type { ProductTreeNode } from '@/frame/components/context/MainContext'
22

3-
// Recursively counts all leaf articles (nodes without children) under a given node
43
export const countArticles = (node: ProductTreeNode): number => {
54
if (node.childPages.length === 0) {
65
return 1

‎src/landings/lib/featured-links.ts‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ type ReqWithFeaturedLinks = {
1414
}
1515
}
1616

17-
// Helper that reshapes the resolved featured-link data placed on the request
18-
// by the `featuredLinks` middleware into the FeaturedLink shape consumed by
19-
// landing page contexts (toc, category, discovery, journey, bespoke).
2017
export const getFeaturedLinksFromReq = (req: unknown): Record<string, Array<FeaturedLink>> => {
2118
const { context } = req as ReqWithFeaturedLinks
2219
return Object.fromEntries(

‎src/landings/lib/fuzzy-match.ts‎

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
// 70% threshold: Raised from 60% to reduce false positives from short queries.
2-
// At 60%, "billing" matched "installing" and "pricing" matched "writing pr descriptions".
3-
// 70% still captures singular/plural ("agent"→"agents" = 80%, "repository"→"repositories" = 73%)
4-
// while filtering the worst noise (both of those false positives were at 67%).
1+
// The 70% threshold keeps singular/plural matches like "agent" to "agents" at 80%
2+
// and "repository" to "repositories" at 73%.
3+
// It rejects noisy matches like "billing" to "installing" and "pricing" to
4+
// "writing pr descriptions", both 67%.
55
const BIGRAM_COVERAGE_THRESHOLD = 0.7
66

7-
// Short search terms produce very few bigrams, making spurious matches likely.
8-
// Require exact substring match for terms with 4 or fewer non-space characters.
7+
// Terms with 4 or fewer non-space characters need exact substring matches
8+
// because they produce too few bigrams.
99
const SHORT_TERM_MAX_LENGTH = 4
1010

1111
const bigramCache = new Map<string, Set<string>>()
1212

13-
// Extract character bigrams from a string, e.g. "agent" -> ["ag", "ge", "en", "nt"].
13+
// Bigrams are adjacent character pairs, so "agent" becomes "ag", "ge", "en", and "nt".
1414
const getBigrams = (str: string): Set<string> => {
1515
const key = str.toLowerCase()
1616
if (bigramCache.has(key)) {
@@ -27,8 +27,8 @@ const getBigrams = (str: string): Set<string> => {
2727
return bigrams
2828
}
2929

30-
// Coverage: what percentage of search bigrams are found in text
31-
// Better for matching short queries against long text
30+
// Bigram coverage measures how many search bigrams appear in text.
31+
// This works better than Jaccard for short queries against longer text.
3232
export const bigramCoverage = (text: string, search: string): number => {
3333
const textBigrams = getBigrams(text)
3434
const searchBigrams = getBigrams(search)
@@ -39,19 +39,15 @@ export const bigramCoverage = (text: string, search: string): number => {
3939
return found / searchBigrams.size
4040
}
4141

42-
// Returns a match score: 1 for exact match, 0-1 for bigram coverage, -1 for no match
42+
// Returns 1 for a substring match, bigram coverage at or above the threshold, or -1 otherwise.
4343
export const fuzzyMatchScore = (text: string, searchTerm: string): number => {
4444
const lowerText = text.toLowerCase()
4545
const lowerSearch = searchTerm.toLowerCase()
4646

4747
if (lowerText.includes(lowerSearch)) return 1
4848

49-
// Short search terms (e.g., "mcp", "pr", "test") produce too few bigrams
50-
// for reliable fuzzy matching, so require exact substring only.
5149
if (lowerSearch.replace(/\s+/g, '').length <= SHORT_TERM_MAX_LENGTH) return -1
5250

53-
// Bigram coverage works better than Jaccard when the text is much longer than
54-
// the search term.
5551
const score = bigramCoverage(text, searchTerm)
5652
return score >= BIGRAM_COVERAGE_THRESHOLD ? score : -1
5753
}
@@ -60,9 +56,8 @@ export const fuzzyMatch = (text: string, searchTerm: string): boolean => {
6056
return fuzzyMatchScore(text, searchTerm) >= 0
6157
}
6258

63-
// Strip stop words from a string, preserving other words.
64-
// On product-specific landing pages (e.g. /copilot), the product name appears
65-
// in nearly every article, drowning out the actual query.
59+
// Product-specific landing pages, such as /copilot, repeat the product name in nearly
60+
// every article, so stop words keep the product name from drowning out the query.
6661
export const stripStopWords = (text: string, stopWords: string[]): string =>
6762
text
6863
.split(/\s+/)

‎src/landings/lib/octicons.ts‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ import {
1414
LockIcon,
1515
} from '@primer/octicons-react'
1616

17-
// The single source of truth for supported octicons. The type and the validation
18-
// array below are both derived from it.
17+
// Derive the type and validation array from this map so supported octicons stay in sync.
1918
export const OCTICON_COMPONENTS = {
2019
bug: BugIcon,
2120
lightbulb: LightBulbIcon,
@@ -40,7 +39,7 @@ export function isValidOcticon(octicon: string | null): octicon is ValidOcticon
4039
return octicon !== null && (octicon as ValidOcticon) in OCTICON_COMPONENTS
4140
}
4241

43-
// Falls back to CopilotIcon for an unknown name.
42+
// Unknown names render CopilotIcon instead of breaking page rendering.
4443
export function getOcticonComponent(octicon: ValidOcticon | undefined) {
4544
if (!octicon || !isValidOcticon(octicon)) {
4645
return CopilotIcon

‎src/landings/middleware/featured-links.ts‎

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,13 @@ import type { Response, NextFunction } from 'express'
33
import type { ExtendedRequest, FeaturedLinkExpanded } from '@/types'
44
import getLinkData from '@/frame/lib/get-link-data'
55

6-
/**
7-
* This is the max. number of featured links, by any category, that we
8-
* display on index landing pages (homepage and TOC landings).
9-
* The reason it's variable is that some featured links are conditional.
10-
* For example:
11-
*
12-
* '/authentication/troubleshooting-ssh',
13-
* '/authentication/connecting-to-github-with...',
14-
* '/authentication/connecting-to-github-with-ssh/a...',
15-
* '{% ifversion ghec %}/authentication/connecting-to-...{% endif %}',
16-
* '/authentication/managing-commit-signature-verif...'
17-
*
18-
* In this case, if we'd "prematurely" sliced that list to the first 4,
19-
* the final result might be 3 items because that conditional one
20-
* would end up being blank and thus omitted.
21-
*
22-
* The reason we don't want to display too many is because it might
23-
* make the landing page columns that lists links far too
24-
* long ("high").
25-
*/
6+
// getLinkData stops after MAX_FEATURED_LINKS resolved links, not frontmatter entries.
7+
// For example, "{% ifversion ghec %}/authentication/troubleshooting-ssh{% endif %}"
8+
// can render blank.
9+
// That keeps a conditional entry that renders blank from leaving a category short,
10+
// while still preventing landing-page columns from growing too tall.
2611
const MAX_FEATURED_LINKS = 4
2712

28-
// This middleware resolves `featuredLinks` from the page's frontmatter into
29-
// a `req.context.featuredLinks` object consumed by the homepage and toc
30-
// landing renderers. It runs for any `index.md` page that defines
31-
// `featuredLinks` in frontmatter.
3213
export default async function featuredLinks(
3314
req: ExtendedRequest,
3415
res: Response,
@@ -54,8 +35,7 @@ export default async function featuredLinks(
5435
{ title: true, intro: true, fullTitle: true },
5536
MAX_FEATURED_LINKS,
5637
)
57-
// We need to use a type assertion here because the Page interfaces are incompatible
58-
// between our local types and the global types, but the actual runtime objects are compatible
38+
// Local and global Page interfaces differ, but the runtime featured-link objects match.
5939
req.context.featuredLinks[key] = (linkData || []) as unknown as FeaturedLinkExpanded[]
6040
}
6141

‎src/landings/pages/home.module.scss‎

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
// Full-bleed gap between the hero and the "All Docs" section. The hero supplies
2-
// the top border (its own border-bottom); this band adds the responsive height
3-
// and the bottom border, both spanning edge-to-edge.
1+
// Full-bleed gap between hero and All Docs. The hero supplies the top border.
2+
// This band adds responsive height and an edge-to-edge bottom border.
43
.sectionGap {
54
height: 1.5rem;
65
border-bottom: var(--brand-borderWidth-thin, 1px) solid
@@ -15,9 +14,8 @@
1514
}
1615
}
1716

18-
// Full-bleed border closing off the bottom of the "All Docs" grid, mirroring the
19-
// gap band at the top. The grid cells only draw rail-width borders, so this
20-
// spans edge-to-edge before the footer.
17+
// Full-bleed border closes the bottom of All Docs to mirror the top gap.
18+
// Grid cells draw rail-width borders, so this spans edge-to-edge before the footer.
2119
.sectionEnd {
2220
height: 4rem;
2321
border-top: var(--brand-borderWidth-thin, 1px) solid

‎src/landings/pages/home.tsx‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ type FeaturedLink = {
2323

2424
type Props = {
2525
mainContext: MainContextT
26-
// Retained in getServerSideProps so the "Getting started" / "Popular" lists
27-
// can be restored later; the Docs 2026 homepage body is just the grid.
26+
// getServerSideProps keeps Getting started and Popular data so the page can restore those lists.
2827
popularLinks: Array<FeaturedLink>
2928
gettingStartedLinks: Array<FeaturedLink>
3029
productGroups: Array<ProductGroupT>

‎src/landings/pages/product.tsx‎

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ import { useRouter } from 'next/router'
66
import type { ExtendedRequest } from '@/types'
77
import type { JourneyTrack } from '@/journeys/lib/journey-path-resolver'
88

9-
// "legacy" javascript needed to maintain existing functionality
10-
// typically operating on elements **within** an article.
9+
// Article pages need these scripts for behavior inside rendered article content.
1110
import copyCode from '@/frame/components/lib/copy-code'
1211
import toggleAnnotation from '@/frame/components/lib/toggle-annotations'
1312

@@ -72,9 +71,9 @@ const GlobalPage = ({
7271
const router = useRouter()
7372

7473
useEffect(() => {
75-
// https://stackoverflow.com/a/67063998
76-
initiateArticleScripts() // on initiate page
77-
router.events.on('routeChangeComplete', initiateArticleScripts) // on client side route
74+
// Mount and route-change init; Next.js keeps this mounted: https://stackoverflow.com/a/67063998
75+
initiateArticleScripts()
76+
router.events.on('routeChangeComplete', initiateArticleScripts)
7877
return () => {
7978
router.events.off('routeChangeComplete', initiateArticleScripts)
8079
}
@@ -118,9 +117,7 @@ const GlobalPage = ({
118117
</ArticleContext.Provider>
119118
)
120119
} else {
121-
// In local dev, when Next.js needs the initial compiled version
122-
// it will request `/_next/static/webpack/$HASH.webpack.hot-update.json`
123-
// or `/_next/webpack-hmr` and then we just let the `content` be undefined.
120+
// Let Next.js hot-reload probes render empty content during local development.
124121
if (
125122
!(router.asPath.startsWith('/_next/static/') || router.asPath.startsWith('/_next/webpack'))
126123
) {
@@ -144,15 +141,14 @@ export const getServerSideProps: GetServerSideProps<Props> = async (context) =>
144141

145142
const additionalUINamespaces: string[] = []
146143

147-
// This looks a little funky, but it's so we only send one context's data to the client
144+
// Send only the active page context to the client to avoid unused page data.
148145
if (currentLayoutName === 'bespoke-landing') {
149146
props.bespokeContext = await getLandingContextFromRequest(req, 'bespoke')
150147
additionalUINamespaces.push('product_landing', 'carousels')
151148
} else if (currentLayoutName === 'journey-landing') {
152149
props.journeyContext = await getLandingContextFromRequest(req, 'journey')
153150

154-
// journey tracks are resolved in middleware and added to the request
155-
// so we need to add them to the journey context here
151+
// Middleware resolves journey tracks, so add them to the journey context before rendering.
156152
const page = req.context?.page as { resolvedJourneyTracks?: JourneyTrack[] } | undefined
157153
if (page?.resolvedJourneyTracks) {
158154
props.journeyContext.journeyTracks = page.resolvedJourneyTracks
@@ -173,7 +169,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async (context) =>
173169
)
174170
}
175171
} else if (props.mainContext.page) {
176-
// All articles that might have hover cards needs this
172+
// Articles need the popovers namespace for hover cards.
177173
additionalUINamespaces.push('popovers')
178174

179175
props.articleContext = getArticleContextFromRequest(

0 commit comments

Comments
 (0)