11/**
2- * Build embeddable font search index from ../../fonts/fonts-index.json
2+ * Build embeddable font search index from ../../fonts/fonts-index.ts
3+ * (falls back to ../../fonts/fonts-index.json)
34 *
45 * Usage: npx tsx src/bin/build-embed-font-index.ts
56 */
67import { existsSync , readFileSync , writeFileSync } from 'fs' ;
78import path from 'path' ;
9+ import { pathToFileURL } from 'url' ;
810
911interface FontIndexEntry {
1012 familyName : string ;
1113 fullName : string ;
1214 postscriptName : string ;
1315 subfamilyName : string ;
1416 ttfPath : string ;
17+ eotPath ?: string ;
1518 searchKeys : string [ ] ;
1619}
1720
@@ -21,28 +24,90 @@ interface FontIndexFile {
2124 fonts : FontIndexEntry [ ] ;
2225}
2326
27+ interface FontIndexModule {
28+ FONT_INDEX ?: FontIndexFile ;
29+ FONT_FAMILY_ALIASES ?: Readonly < Record < string , string > > ;
30+ EXTRA_SEARCH_ALIASES ?: Readonly < Record < string , string > > ;
31+ }
32+
2433function toSearchKey ( name : string ) : string {
2534 return name
2635 . normalize ( 'NFKC' )
2736 . toLowerCase ( )
2837 . replace ( / [ ^ a - z 0 - 9 \u4e00 - \u9fff ] / g, '' ) ;
2938}
3039
31- function main ( ) : void {
40+ async function loadFontIndex (
41+ fontsDir : string
42+ ) : Promise < {
43+ data : FontIndexFile ;
44+ familyAliases : Record < string , string > ;
45+ extraSearchAliases : Record < string , string > ;
46+ sourceLabel : string ;
47+ } > {
48+ const tsPath = path . join ( fontsDir , 'fonts-index.ts' ) ;
49+ const jsonPath = path . join ( fontsDir , 'fonts-index.json' ) ;
50+
51+ if ( existsSync ( tsPath ) ) {
52+ const mod = ( await import ( pathToFileURL ( tsPath ) . href ) ) as FontIndexModule ;
53+ if ( ! mod . FONT_INDEX ) {
54+ throw new Error ( `${ tsPath } must export FONT_INDEX` ) ;
55+ }
56+ return {
57+ data : mod . FONT_INDEX ,
58+ familyAliases : { ...( mod . FONT_FAMILY_ALIASES ?? { } ) } ,
59+ extraSearchAliases : { ...( mod . EXTRA_SEARCH_ALIASES ?? { } ) } ,
60+ sourceLabel : '../../fonts/fonts-index.ts' ,
61+ } ;
62+ }
63+
64+ if ( ! existsSync ( jsonPath ) ) {
65+ throw new Error ( `Missing font index: ${ tsPath } or ${ jsonPath } ` ) ;
66+ }
67+
68+ const raw = readFileSync ( jsonPath , 'utf8' ) ;
69+ return {
70+ data : JSON . parse ( raw ) as FontIndexFile ,
71+ familyAliases : { } ,
72+ extraSearchAliases : { } ,
73+ sourceLabel : '../../fonts/fonts-index.json' ,
74+ } ;
75+ }
76+
77+ function addAliasMappings (
78+ searchIndex : Record < string , string > ,
79+ familyAliases : Record < string , string > ,
80+ extraSearchAliases : Record < string , string >
81+ ) : void {
82+ for ( const [ alias , familyName ] of Object . entries ( familyAliases ) ) {
83+ const keys = [ toSearchKey ( alias ) , alias . trim ( ) . toLowerCase ( ) ] ;
84+ for ( const key of keys ) {
85+ if ( ! key || key . length < 2 ) continue ;
86+ if ( ! searchIndex [ key ] ) searchIndex [ key ] = familyName ;
87+ }
88+ }
89+
90+ for ( const [ aliasKey , familyName ] of Object . entries ( extraSearchAliases ) ) {
91+ const key = toSearchKey ( aliasKey ) ;
92+ if ( ! key || key . length < 2 ) continue ;
93+ if ( ! searchIndex [ key ] ) searchIndex [ key ] = familyName ;
94+ }
95+ }
96+
97+ async function main ( ) : Promise < void > {
3298 const repoRoot = path . resolve ( __dirname , '../../../..' ) ;
33- const indexPath = path . join ( repoRoot , 'fonts' , 'fonts-index.json ') ;
99+ const fontsDir = path . join ( repoRoot , 'fonts' ) ;
34100 const outPath = path . resolve ( __dirname , '../utils/embedFonts.index.ts' ) ;
35101
36- if ( ! existsSync ( indexPath ) ) {
102+ if ( ! existsSync ( path . join ( fontsDir , 'fonts-index.ts' ) ) && ! existsSync ( path . join ( fontsDir , 'fonts-index.json' ) ) ) {
37103 if ( existsSync ( outPath ) ) {
38- console . log ( `Skip: ${ indexPath } not found, keeping existing ${ outPath } ` ) ;
104+ console . log ( `Skip: fonts index not found in ${ fontsDir } , keeping existing ${ outPath } ` ) ;
39105 return ;
40106 }
41- throw new Error ( `Missing font index: ${ indexPath } ` ) ;
107+ throw new Error ( `Missing font index in ${ fontsDir } ` ) ;
42108 }
43109
44- const raw = readFileSync ( indexPath , 'utf8' ) ;
45- const data = JSON . parse ( raw ) as FontIndexFile ;
110+ const { data, familyAliases, extraSearchAliases, sourceLabel } = await loadFontIndex ( fontsDir ) ;
46111
47112 const searchIndex : Record < string , string > = { } ;
48113 const families = new Set < string > ( ) ;
@@ -62,21 +127,25 @@ function main(): void {
62127 }
63128 }
64129
130+ addAliasMappings ( searchIndex , familyAliases , extraSearchAliases ) ;
131+
65132 const sortedKeys = Object . keys ( searchIndex ) . sort ( ) ;
66133 const lines = sortedKeys . map (
67134 ( key ) => ` ${ JSON . stringify ( key ) } : ${ JSON . stringify ( searchIndex [ key ] ) } ,`
68135 ) ;
69136
70137 const content = `/**
71138 * Auto-generated by src/bin/build-embed-font-index.ts
72- * Source: ../../fonts/fonts-index.json
139+ * Source: ${ sourceLabel }
73140 * Do not edit manually.
74141 */
75142
76143export const EMBED_FONT_INDEX_META = {
77144 fontCount: ${ data . count } ,
78145 familyCount: ${ families . size } ,
79146 searchKeyCount: ${ sortedKeys . length } ,
147+ familyAliasCount: ${ Object . keys ( familyAliases ) . length } ,
148+ extraSearchAliasCount: ${ Object . keys ( extraSearchAliases ) . length } ,
80149 timestamp: ${ JSON . stringify ( data . timestamp ) } ,
81150} as const;
82151
@@ -87,8 +156,13 @@ ${lines.join('\n')}
87156
88157 writeFileSync ( outPath , content , 'utf8' ) ;
89158 console . log (
90- `Wrote ${ outPath } (${ families . size } families, ${ sortedKeys . length } search keys)`
159+ `Wrote ${ outPath } (${ families . size } families, ${ sortedKeys . length } search keys, ` +
160+ `${ Object . keys ( familyAliases ) . length } family aliases, ` +
161+ `${ Object . keys ( extraSearchAliases ) . length } extra search aliases)`
91162 ) ;
92163}
93164
94- main ( ) ;
165+ main ( ) . catch ( ( error ) => {
166+ console . error ( error ) ;
167+ process . exit ( 1 ) ;
168+ } ) ;
0 commit comments