Skip to content

Commit 7185c4e

Browse files
authored
Merge pull request #10250 from quarto-dev/feature/brand-yaml
`_brand.yml` - schemas/validation, storage, access in Lua
2 parents 1980e95 + b4906a8 commit 7185c4e

File tree

13 files changed

+2158
-12
lines changed

13 files changed

+2158
-12
lines changed

src/command/render/filters.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { existsSync } from "fs/mod.ts";
88

99
import {
1010
kBibliography,
11+
kBrand,
1112
kCitationLocation,
1213
kCiteMethod,
1314
kClearCellOptions,
@@ -192,6 +193,7 @@ export async function filterParamsJson(
192193
[kIsShinyPython]: isShinyPython,
193194
[kShinyPythonExec]: isShinyPython ? await pythonExec() : undefined,
194195
[kExecutionEngine]: options.executionEngine,
196+
[kBrand]: options.format.render[kBrand],
195197
};
196198
return JSON.stringify(params);
197199
}

src/command/render/render-contexts.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* render-contexts.ts
33
*
4-
* Copyright (C) 2021-2023 Posit Software, PBC
4+
* Copyright (C) 2021-2024 Posit Software, PBC
55
*/
66

77
import { Format, FormatExecute, Metadata } from "../../config/types.ts";
@@ -89,6 +89,7 @@ import {
8989
} from "../../core/pandoc/pandoc-formats.ts";
9090
import { ExtensionContext } from "../../extension/types.ts";
9191
import { NotebookContext } from "../../render/notebook/notebook-types.ts";
92+
import { Brand } from "../../resources/types/schema-types.ts";
9293

9394
export async function resolveFormatsFromMetadata(
9495
metadata: Metadata,
@@ -394,7 +395,7 @@ async function resolveFormats(
394395
engine: ExecutionEngine,
395396
options: RenderOptions,
396397
_notebookContext: NotebookContext,
397-
project?: ProjectContext,
398+
project: ProjectContext,
398399
enforceProjectFormats: boolean = true,
399400
): Promise<Record<string, { format: Format; active: boolean }>> {
400401
// input level metadata
@@ -598,6 +599,9 @@ async function resolveFormats(
598599
);
599600
};
600601

602+
// resolve brand in project and forward it to format
603+
mergedFormats[format].render.brand = await project.resolveBrand();
604+
601605
// ensure that we have a valid forma
602606
const formatIsValid = isValidFormat(
603607
formatDesc,

src/config/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ export const kPreferHtml = "prefer-html";
109109
export const kSelfContainedMath = "self-contained-math";
110110
export const kBiblioConfig = "biblio-config";
111111
export const kBodyClasses = "body-classes";
112+
export const kBrand = "brand";
112113

113114
export const kLatexAutoMk = "latex-auto-mk";
114115
export const kLatexAutoInstall = "latex-auto-install";

src/config/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
kBackToTop,
1414
kBaseFormat,
1515
kBodyClasses,
16+
kBrand,
1617
kCache,
1718
kCalloutCautionCaption,
1819
kCalloutImportantCaption,
@@ -246,6 +247,7 @@ import {
246247
import { HtmlPostProcessor, RenderServices } from "../command/render/types.ts";
247248
import { QuartoFilterSpec } from "../command/render/types.ts";
248249
import { ProjectContext } from "../project/types.ts";
250+
import { Brand } from "../resources/types/schema-types.ts";
249251

250252
export const kDependencies = "dependencies";
251253
export const kSassBundles = "sass-bundles";
@@ -493,6 +495,7 @@ export interface FormatRender {
493495
[kValidateYaml]?: boolean;
494496
[kCanonicalUrl]?: boolean | string;
495497
[kBodyClasses]?: string;
498+
[kBrand]?: Brand;
496499
}
497500

498501
export interface FormatExecute {

src/project/project-context.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import {
6666
ignoreFieldsForProjectType,
6767
normalizeFormatYaml,
6868
projectConfigFile,
69+
projectResolveBrand,
6970
projectResolveFullMarkdownForFile,
7071
projectVarsFile,
7172
} from "./project-shared.ts";
@@ -254,6 +255,7 @@ export async function projectContext(
254255
}
255256

256257
const result: ProjectContext = {
258+
resolveBrand: async () => projectResolveBrand(result),
257259
resolveFullMarkdownForFile: (
258260
engine: ExecutionEngine | undefined,
259261
file: string,
@@ -333,6 +335,7 @@ export async function projectContext(
333335
} else {
334336
debug(`projectContext: Found Quarto project in ${dir}`);
335337
const result: ProjectContext = {
338+
resolveBrand: async () => projectResolveBrand(result),
336339
resolveFullMarkdownForFile: (
337340
engine: ExecutionEngine | undefined,
338341
file: string,
@@ -390,6 +393,7 @@ export async function projectContext(
390393
configResolvers.shift();
391394
} else if (force) {
392395
const context: ProjectContext = {
396+
resolveBrand: async () => projectResolveBrand(context),
393397
resolveFullMarkdownForFile: (
394398
engine: ExecutionEngine | undefined,
395399
file: string,

src/project/project-shared.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ import { mappedIndexToLineCol } from "../core/lib/mapped-text.ts";
4646
import { normalizeNewlines } from "../core/lib/text.ts";
4747
import { DirectiveCell } from "../core/lib/break-quarto-md-types.ts";
4848
import { QuartoJSONSchema } from "../core/yaml.ts";
49+
import { refSchema } from "../core/lib/yaml-schema/common.ts";
50+
import { Brand } from "../resources/types/schema-types.ts";
4951

5052
export function projectExcludeDirs(context: ProjectContext): string[] {
5153
const outputDir = projectOutputDir(context);
@@ -484,3 +486,23 @@ const ensureFileInformationCache = (project: ProjectContext, file: string) => {
484486
}
485487
return project.fileInformationCache.get(file)!;
486488
};
489+
490+
export async function projectResolveBrand(project: ProjectContext) {
491+
if (project.brandCache) {
492+
return project.brandCache.brand;
493+
}
494+
project.brandCache = {};
495+
for (const brandFile of ["_brand.yml", "_brand.yaml"]) {
496+
const brandPath = join(project.dir, brandFile);
497+
if (!existsSync(brandPath)) {
498+
continue;
499+
}
500+
const brand = await readAndValidateYamlFromFile(
501+
brandPath,
502+
refSchema("brand", "Format-independent brand configuration."),
503+
"Brand validation failed for " + brandPath + ".",
504+
) as Brand;
505+
project.brandCache.brand = brand;
506+
}
507+
return project.brandCache.brand;
508+
}

src/project/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { ExecutionEngine, ExecutionTarget } from "../execute/types.ts";
1313
import { InspectedMdCell } from "../quarto-core/inspect-types.ts";
1414
import { NotebookContext } from "../render/notebook/notebook-types.ts";
1515
import {
16+
Brand,
1617
NavigationItem as NavItem,
1718
NavigationItemObject,
1819
NavigationItemObject as SidebarTool,
@@ -66,6 +67,10 @@ export interface ProjectContext {
6667

6768
fileInformationCache: Map<string, FileInformation>;
6869

70+
// This is a cache of _brand.yml for a project
71+
brandCache?: { brand?: Brand };
72+
resolveBrand: () => Promise<Brand | undefined>;
73+
6974
// expands markdown for a file
7075
// input file doesn't have to be markdown; it can be, for example, a knitr spin file
7176
// output file is always markdown, though, and it is cached in the project

src/project/types/single-file/single-file.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ import { renderFormats } from "../../../command/render/render-contexts.ts";
1919
import { RenderFlags } from "../../../command/render/types.ts";
2020
import { MappedString } from "../../../core/mapped-text.ts";
2121
import { fileExecutionEngineAndTarget } from "../../../execute/engine.ts";
22-
import { projectResolveFullMarkdownForFile } from "../../project-shared.ts";
22+
import {
23+
projectResolveBrand,
24+
projectResolveFullMarkdownForFile,
25+
} from "../../project-shared.ts";
2326
import { ExecutionEngine } from "../../../execute/types.ts";
2427

2528
export function singleFileProjectContext(
@@ -30,6 +33,7 @@ export function singleFileProjectContext(
3033
const environmentMemoizer = makeProjectEnvironmentMemoizer(notebookContext);
3134

3235
const result: ProjectContext = {
36+
resolveBrand: () => projectResolveBrand(result),
3337
dir: normalizePath(dirname(source)),
3438
engines: [],
3539
files: {

0 commit comments

Comments
 (0)