Skip to content

Commit aca2088

Browse files
authored
Merge pull request #11027 from quarto-dev/feature/brand-yaml-schema-consolidation
Feature/brand yaml schema consolidation
2 parents 52d5a6e + b2d4797 commit aca2088

File tree

15 files changed

+954
-605
lines changed

15 files changed

+954
-605
lines changed

src/core/brand/brand.ts

Lines changed: 85 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@
99
import {
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+
4551
type ProcessedBrandData = {
4652
color: Record<string, string>;
4753
typography: BrandTypography;
48-
logo: Record<string, BrandStringLightDark>;
54+
logo: Record<string, CanonicalLogoInfo>;
4955
};
5056

5157
export 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
}

src/core/sass/brand.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,9 +285,11 @@ const brandTypographyBundle = (
285285
const resolveHTMLFontInformation = (
286286
kind: FontKind,
287287
): HTMLFontInformation | undefined => {
288-
const resolvedFontOptions = brand.data.typography?.[kind];
288+
let resolvedFontOptions = brand.data.typography?.[kind];
289289
if (!resolvedFontOptions) {
290290
return undefined;
291+
} else if (typeof resolvedFontOptions === "string") {
292+
resolvedFontOptions = { family: resolvedFontOptions };
291293
}
292294
const family = resolvedFontOptions.family;
293295
const font = getFontFamilies(family);

src/format/reveal/format-reveal.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ const determineRevealLogo = (format: Format): string | undefined => {
335335
return logoInfo;
336336
} else {
337337
// what to do about light vs dark?
338-
return logoInfo.light ?? logoInfo.dark;
338+
return logoInfo?.light.path ?? logoInfo?.dark.path;
339339
}
340340
}
341341
}

src/project/types/website/website-shared.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,9 @@ export async function websiteNavigationConfig(project: ProjectContext) {
172172
const logo = projectBrand.processedData.logo.medium ??
173173
projectBrand.processedData.logo.small ??
174174
projectBrand.processedData.logo.large;
175-
if (typeof logo === "string") {
176-
sidebars[0].logo = logo;
177-
} else if (typeof logo === "object") {
178-
sidebars[0].logo = logo.light; // TODO: This needs smarts to work on light+dark themes
175+
if (logo) {
176+
sidebars[0].logo = logo.light.path; // TODO: This needs smarts to work on light+dark themes
177+
sidebars[0]["logo-alt"] = logo.light.alt;
179178
}
180179
}
181180
}
@@ -186,10 +185,9 @@ export async function websiteNavigationConfig(project: ProjectContext) {
186185
const logo = projectBrand.processedData.logo.small ??
187186
projectBrand.processedData.logo.medium ??
188187
projectBrand.processedData.logo.large;
189-
if (typeof logo === "string") {
190-
navbar.logo = logo;
191-
} else if (typeof logo === "object") {
192-
navbar.logo = logo.light; // TODO: This needs smarts to work on light+dark themes
188+
if (logo) {
189+
navbar.logo = logo.light.path; // TODO: This needs smarts to work on light+dark themes
190+
navbar["logo-alt"] = logo.light.alt;
193191
}
194192
}
195193

0 commit comments

Comments
 (0)