@@ -11,20 +11,34 @@ const MAX_LINKS = 3
1111const SUBJECT_SCORE = 2
1212const MENTION_SCORE = 1
1313
14- /** Whole-word, case-sensitive match on any of the competitor's names. */
15- function buildMentionPattern ( competitor : CompetitorProfile ) : RegExp {
16- const phrases = competitor . mentions ?? [ competitor . name ]
14+ interface MentionPatterns {
15+ /** Matches Title Case titles and tags, where a bare common-word name ("Make") is ambiguous. */
16+ subject : RegExp
17+ /** Matches sentence-case descriptions, where the bare name is unambiguous. */
18+ mention : RegExp
19+ }
20+
21+ /** Whole-word, case-sensitive match on any of `phrases`. */
22+ function wordPattern ( phrases : string [ ] ) : RegExp {
1723 return new RegExp ( `\\b(?:${ phrases . map ( escapeRegExp ) . join ( '|' ) } )\\b` )
1824}
1925
26+ function buildMentionPatterns ( competitor : CompetitorProfile ) : MentionPatterns {
27+ const aliases = competitor . mentions ?? [ competitor . name ]
28+ return {
29+ subject : wordPattern ( aliases ) ,
30+ mention : wordPattern ( [ competitor . name , ...aliases ] ) ,
31+ }
32+ }
33+
2034const COMPETITOR_PATTERNS = ALL_COMPETITORS . map ( ( competitor ) => ( {
2135 competitor,
22- pattern : buildMentionPattern ( competitor ) ,
36+ patterns : buildMentionPatterns ( competitor ) ,
2337} ) )
2438
25- function scoreRelevance ( post : ContentMeta , pattern : RegExp ) : number {
26- if ( pattern . test ( post . title ) || post . tags . some ( ( tag ) => pattern . test ( tag ) ) ) return SUBJECT_SCORE
27- return pattern . test ( post . description ) ? MENTION_SCORE : 0
39+ function scoreRelevance ( post : ContentMeta , { subject , mention } : MentionPatterns ) : number {
40+ if ( subject . test ( post . title ) || post . tags . some ( ( tag ) => subject . test ( tag ) ) ) return SUBJECT_SCORE
41+ return mention . test ( post . description ) ? MENTION_SCORE : 0
2842}
2943
3044/** The highest-scoring items with a positive score, best first; ties keep input order. */
@@ -44,16 +58,16 @@ function topByScore<T>(items: T[], score: (item: T) => number): T[] {
4458export async function getLibraryPostsForCompetitor (
4559 competitor : CompetitorProfile
4660) : Promise < ContentMeta [ ] > {
47- const pattern = buildMentionPattern ( competitor )
48- return topByScore ( await getAllPostMeta ( ) , ( post ) => scoreRelevance ( post , pattern ) )
61+ const patterns = buildMentionPatterns ( competitor )
62+ return topByScore ( await getAllPostMeta ( ) , ( post ) => scoreRelevance ( post , patterns ) )
4963}
5064
5165/**
5266 * Comparison pages for the competitors a library article is about or names,
5367 * subjects first, then in {@link ALL_COMPETITORS} order.
5468 */
5569export function getComparisonsForPost ( post : ContentMeta ) : CompetitorProfile [ ] {
56- return topByScore ( COMPETITOR_PATTERNS , ( { pattern } ) => scoreRelevance ( post , pattern ) ) . map (
70+ return topByScore ( COMPETITOR_PATTERNS , ( { patterns } ) => scoreRelevance ( post , patterns ) ) . map (
5771 ( { competitor } ) => competitor
5872 )
5973}
0 commit comments