99import {
1010 Brand as BrandJson ,
1111 BrandFont ,
12+ BrandLogoExplicitResource ,
1213 BrandNamedLogo ,
1314 BrandNamedThemeColor ,
1415 BrandStringLightDark ,
1516 BrandTypography ,
1617 BrandTypographyOptionsBase ,
1718 BrandTypographyOptionsHeadings ,
1819} from "../../resources/types/schema-types.ts" ;
20+ import { InternalError } from "../lib/error.ts" ;
1921
2022// we can't programmatically convert typescript types to string arrays,
2123// so we have to define this manually. They should match `BrandNamedThemeColor` in schema-types.ts
@@ -32,7 +34,6 @@ export const defaultColorNames: BrandNamedThemeColor[] = [
3234 "danger" ,
3335 "light" ,
3436 "dark" ,
35- "emphasis" ,
3637 "link" ,
3738] ;
3839
@@ -42,10 +43,15 @@ const defaultLogoNames: string[] = [
4243 "large" ,
4344] ;
4445
46+ type CanonicalLogoInfo = {
47+ light : BrandLogoExplicitResource ;
48+ dark : BrandLogoExplicitResource ;
49+ } ;
50+
4551type ProcessedBrandData = {
4652 color : Record < string , string > ;
4753 typography : BrandTypography ;
48- logo : Record < string , BrandStringLightDark > ;
54+ logo : Record < string , CanonicalLogoInfo > ;
4955} ;
5056
5157export class Brand {
@@ -86,34 +92,65 @@ export class Brand {
8692 if ( link ) {
8793 typography . link = link ;
8894 }
89- const monospace = this . getFont ( "monospace" ) ;
95+ let monospace = this . getFont ( "monospace" ) ;
96+ let monospaceInline = this . getFont ( "monospace-inline" ) ;
97+ let monospaceBlock = this . getFont ( "monospace-block" ) ;
98+
9099 if ( monospace ) {
100+ if ( typeof monospace === "string" ) {
101+ monospace = { family : monospace } ;
102+ }
91103 typography . monospace = monospace ;
92104 }
93- const monospaceInline = this . getFont ( "monospace-inline" ) ;
105+ if ( monospaceInline && typeof monospaceInline === "string" ) {
106+ monospaceInline = { family : monospaceInline } ;
107+ }
108+ if ( monospaceBlock && typeof monospaceBlock === "string" ) {
109+ monospaceBlock = { family : monospaceBlock } ;
110+ }
111+
112+ // cut off control flow here so the type checker knows these
113+ // are not strings
114+ if ( typeof monospace === "string" ) {
115+ throw new InternalError ( "should never happen" ) ;
116+ }
117+ if ( typeof monospaceInline === "string" ) {
118+ throw new InternalError ( "should never happen" ) ;
119+ }
120+ if ( typeof monospaceBlock === "string" ) {
121+ throw new InternalError ( "should never happen" ) ;
122+ }
123+
94124 if ( monospace || monospaceInline ) {
95125 typography [ "monospace-inline" ] = {
96126 ...( monospace ?? { } ) ,
97127 ...( monospaceInline ?? { } ) ,
98128 } ;
99129 }
100- const monospaceBlock = this . getFont ( "monospace-block" ) ;
130+ if ( monospaceBlock ) {
131+ if ( typeof monospaceBlock === "string" ) {
132+ monospaceBlock = { family : monospaceBlock } ;
133+ }
134+ }
101135 if ( monospace || monospaceBlock ) {
102136 typography [ "monospace-block" ] = {
103137 ...( monospace ?? { } ) ,
104138 ...( monospaceBlock ?? { } ) ,
105139 } ;
106140 }
107141
108- const logo : Record < string , BrandStringLightDark > = { } ;
109- for ( const logoName of Object . keys ( data . logo ?. images ?? { } ) ) {
110- logo [ logoName ] = this . getLogo ( logoName ) ;
111- }
112- for ( const logoName of Object . keys ( data . logo ?? { } ) ) {
113- if ( logoName === "images" ) {
114- continue ;
142+ const logo : Record < string , CanonicalLogoInfo > = { } ;
143+ for (
144+ const size of [
145+ "small" ,
146+ "medium" ,
147+ "large" ,
148+ ] as ( "small" | "medium" | "large" ) [ ]
149+ ) {
150+ const v = this . getLogo ( size ) ;
151+ if ( v ) {
152+ logo [ size ] = v ;
115153 }
116- logo [ logoName ] = this . getLogo ( logoName ) ;
117154 }
118155
119156 return {
@@ -195,61 +232,42 @@ export class Brand {
195232 return fonts ?? [ ] ;
196233 }
197234
235+ getLogoResource ( name : string ) : BrandLogoExplicitResource {
236+ const entry = this . data . logo ?. images ?. [ name ] ;
237+ if ( ! entry ) {
238+ return { path : name } ;
239+ }
240+ if ( typeof entry === "string" ) {
241+ return { path : entry } ;
242+ }
243+ return entry ;
244+ }
245+
198246 // the same implementation as getColor except we can also return {light,dark}
199247 // assuming for now that with only contains strings, not {light,dark}
200- getLogo ( name : string ) : BrandStringLightDark {
201- const seenValues = new Set < string > ( ) ;
202- do {
203- if ( seenValues . has ( name ) ) {
204- throw new Error (
205- `Circular reference in _brand.yml color definitions: ${
206- Array . from ( seenValues ) . join (
207- " -> " ,
208- )
209- } `,
210- ) ;
211- }
212- seenValues . add ( name ) ;
213- if ( this . data . logo ?. images ?. [ name ] ) {
214- name = this . data . logo . images [ name ] as string ;
215- } else if (
216- defaultLogoNames . includes ( name as BrandNamedLogo ) &&
217- this . data . logo ?. [ name as BrandNamedLogo ]
218- ) {
219- const brandSLD : BrandStringLightDark = this . data
220- . logo [ name as BrandNamedLogo ] ! ;
221- if ( typeof brandSLD == "string" ) {
222- name = brandSLD ;
223- } else {
224- const ret : BrandStringLightDark = { } ;
225- // we need to actually-recurse and not just use the loop
226- // because two paths light/dark
227- const light = brandSLD . light ;
228- if ( light ) {
229- const brandSLD2 = this . getLogo ( light ) ;
230- if ( typeof brandSLD2 == "string" ) {
231- ret . light = brandSLD2 ;
232- } else {
233- ret . light = brandSLD2 . light ;
234- }
235- }
236- const dark = brandSLD . dark ;
237- if ( dark ) {
238- const brandSLD2 = this . getLogo ( dark ) ;
239- if ( typeof brandSLD2 == "string" ) {
240- ret . dark = brandSLD2 ;
241- } else {
242- ret . dark = brandSLD2 . light ;
243- }
244- }
245- return ret ;
246- }
247- } else {
248- return name ;
249- }
250- } while ( seenValues . size < 100 ) ; // 100 ought to be enough for anyone, with apologies to Bill Gates
251- throw new Error (
252- "Recursion depth exceeded 100 in _brand.yml logo definitions" ,
253- ) ;
248+ getLogo ( name : "small" | "medium" | "large" ) : CanonicalLogoInfo | undefined {
249+ const entry = this . data . logo ?. [ name ] ;
250+ if ( ! entry ) {
251+ return undefined ;
252+ }
253+ if ( typeof entry === "string" ) {
254+ const res = this . getLogoResource ( entry ) ;
255+ return {
256+ light : res ,
257+ dark : res ,
258+ } ;
259+ }
260+ const lightEntry = entry ?. light
261+ ? this . getLogoResource ( entry . light )
262+ : undefined ;
263+ const darkEntry = entry ?. dark
264+ ? this . getLogoResource ( entry . dark )
265+ : undefined ;
266+ if ( lightEntry && darkEntry ) {
267+ return {
268+ light : lightEntry ,
269+ dark : darkEntry ,
270+ } ;
271+ }
254272 }
255273}
0 commit comments